roojs-bootstrap.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     
19656     getChildContainer : function()
19657     {
19658         return this.el.select('.popover-content',true).first();
19659     },
19660     
19661     getAutoCreate : function(){
19662          
19663         var cfg = {
19664            cls : 'popover roo-dynamic shadow roo-popover',
19665            style: 'display:block',
19666            cn : [
19667                 {
19668                     cls : 'arrow'
19669                 },
19670                 {
19671                     cls : 'popover-inner ',
19672                     cn : [
19673                         {
19674                             tag: 'h3',
19675                             cls: 'popover-title popover-header',
19676                             html : this.title || ''
19677                         },
19678                         {
19679                             cls : 'popover-content popover-body'  + this.cls,
19680                             html : this.html || ''
19681                         }
19682                     ]
19683                     
19684                 }
19685            ]
19686         };
19687         
19688         return cfg;
19689     },
19690     /**
19691      * @param {string} the title
19692      */
19693     setTitle: function(str)
19694     {
19695         this.title = str;
19696         if (this.el) {
19697             this.el.select('.popover-title',true).first().dom.innerHTML = str;
19698         }
19699         
19700     },
19701     /**
19702      * @param {string} the body content
19703      */
19704     setContent: function(str)
19705     {
19706         this.html = str;
19707         if (this.el) {
19708             this.el.select('.popover-content',true).first().dom.innerHTML = str;
19709         }
19710         
19711     },
19712     // as it get's added to the bottom of the page.
19713     onRender : function(ct, position)
19714     {
19715         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19716         if(!this.el){
19717             var cfg = Roo.apply({},  this.getAutoCreate());
19718             cfg.id = Roo.id();
19719             
19720             if (this.cls) {
19721                 cfg.cls += ' ' + this.cls;
19722             }
19723             if (this.style) {
19724                 cfg.style = this.style;
19725             }
19726             //Roo.log("adding to ");
19727             this.el = Roo.get(document.body).createChild(cfg, position);
19728 //            Roo.log(this.el);
19729         }
19730         
19731         var nitems = [];
19732         if(typeof(this.items) != 'undefined'){
19733             var items = this.items;
19734             delete this.items;
19735
19736             for(var i =0;i < items.length;i++) {
19737                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19738             }
19739         }
19740
19741         this.items = nitems;
19742         
19743         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19744         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19745         
19746         
19747         this.initEvents();
19748     },
19749     
19750     resizeMask : function()
19751     {
19752         this.maskEl.setSize(
19753             Roo.lib.Dom.getViewWidth(true),
19754             Roo.lib.Dom.getViewHeight(true)
19755         );
19756     },
19757     
19758     initEvents : function()
19759     {
19760          
19761         Roo.bootstrap.Popover.register(this);
19762          
19763         
19764         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19765         this.el.enableDisplayMode('block');
19766         this.el.hide();
19767         if (this.over === false && !this.parent()) {
19768             return; 
19769         }
19770         if (this.triggers === false) {
19771             return;
19772         }
19773          
19774         // support parent
19775         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19776         var triggers = this.trigger ? this.trigger.split(' ') : [];
19777         Roo.each(triggers, function(trigger) {
19778         
19779             if (trigger == 'click') {
19780                 on_el.on('click', this.toggle, this);
19781             } else if (trigger != 'manual') {
19782                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19783                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19784       
19785                 on_el.on(eventIn  ,this.enter, this);
19786                 on_el.on(eventOut, this.leave, this);
19787             }
19788         }, this);
19789         
19790     },
19791     
19792     
19793     // private
19794     timeout : null,
19795     hoverState : null,
19796     
19797     toggle : function () {
19798         this.hoverState == 'in' ? this.leave() : this.enter();
19799     },
19800     
19801     enter : function () {
19802         
19803         clearTimeout(this.timeout);
19804     
19805         this.hoverState = 'in';
19806     
19807         if (!this.delay || !this.delay.show) {
19808             this.show();
19809             return;
19810         }
19811         var _t = this;
19812         this.timeout = setTimeout(function () {
19813             if (_t.hoverState == 'in') {
19814                 _t.show();
19815             }
19816         }, this.delay.show)
19817     },
19818     
19819     leave : function() {
19820         clearTimeout(this.timeout);
19821     
19822         this.hoverState = 'out';
19823     
19824         if (!this.delay || !this.delay.hide) {
19825             this.hide();
19826             return;
19827         }
19828         var _t = this;
19829         this.timeout = setTimeout(function () {
19830             if (_t.hoverState == 'out') {
19831                 _t.hide();
19832             }
19833         }, this.delay.hide)
19834     },
19835     /**
19836      * Show the popover
19837      * @param {Roo.Element|string|false} - element to align and point to.
19838      */
19839     show : function (on_el)
19840     {
19841         
19842         on_el = on_el || false; // default to false
19843         if (!on_el) {
19844             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19845                 on_el = this.parent().el;
19846             } else if (this.over) {
19847                 Roo.get(this.over);
19848             }
19849             
19850         }
19851         
19852         if (!this.el) {
19853             this.render(document.body);
19854         }
19855         
19856         
19857         this.el.removeClass([
19858             'fade','top','bottom', 'left', 'right','in',
19859             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19860         ]);
19861         
19862         if (!this.title.length) {
19863             this.el.select('.popover-title',true).hide();
19864         }
19865         
19866         
19867         var placement = typeof this.placement == 'function' ?
19868             this.placement.call(this, this.el, on_el) :
19869             this.placement;
19870             
19871         /*
19872         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19873         
19874         // I think  'auto right' - but 
19875         
19876         var autoPlace = autoToken.test(placement);
19877         if (autoPlace) {
19878             placement = placement.replace(autoToken, '') || 'top';
19879         }
19880         */
19881         
19882         
19883         this.el.show();
19884         this.el.dom.style.display='block';
19885         
19886         //this.el.appendTo(on_el);
19887         
19888         var p = this.getPosition();
19889         var box = this.el.getBox();
19890         
19891         
19892         var align = Roo.bootstrap.Popover.alignment[placement];
19893         this.el.addClass(align[2]);
19894
19895 //        Roo.log(align);
19896
19897         if (on_el) {
19898             this.el.alignTo(on_el, align[0],align[1]);
19899         } else {
19900             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19901             var es = this.el.getSize();
19902             var x = Roo.lib.Dom.getViewWidth()/2;
19903             var y = Roo.lib.Dom.getViewHeight()/2;
19904             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19905             
19906         }
19907
19908         
19909         //var arrow = this.el.select('.arrow',true).first();
19910         //arrow.set(align[2], 
19911         
19912         this.el.addClass('in');
19913         
19914         
19915         if (this.el.hasClass('fade')) {
19916             // fade it?
19917         }
19918         
19919         this.hoverState = 'in';
19920         
19921         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19922         if (this.modal) {
19923             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19924             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19925             this.maskEl.dom.style.display = 'block';
19926             this.maskEl.addClass('show');
19927         }
19928         
19929         
19930         
19931         this.fireEvent('show', this);
19932         
19933     },
19934     hide : function()
19935     {
19936         this.el.setXY([0,0]);
19937         this.el.removeClass('in');
19938         this.el.hide();
19939         this.hoverState = null;
19940         this.maskEl.hide(); // always..
19941         this.fireEvent('hide', this);
19942     }
19943     
19944 });
19945
19946
19947 Roo.apply(Roo.bootstrap.Popover, {
19948
19949     alignment : {
19950         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19951         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19952         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19953         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19954     },
19955     
19956     zIndex : 20001,
19957
19958     clickHander : false,
19959     
19960
19961     onMouseDown : function(e)
19962     {
19963         if (!e.getTarget(".roo-popover")) {
19964             this.hideAll();
19965         }
19966          
19967     },
19968     
19969     popups : [],
19970     
19971     register : function(popup)
19972     {
19973         if (!Roo.bootstrap.Popover.clickHandler) {
19974             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19975         }
19976         // hide other popups.
19977         this.hideAll();
19978         this.popups.push(popup);
19979     },
19980     hideAll : function()
19981     {
19982         this.popups.forEach(function(p) {
19983             p.hide();
19984         });
19985     }
19986
19987 });/*
19988  * - LGPL
19989  *
19990  * Progress
19991  * 
19992  */
19993
19994 /**
19995  * @class Roo.bootstrap.Progress
19996  * @extends Roo.bootstrap.Component
19997  * Bootstrap Progress class
19998  * @cfg {Boolean} striped striped of the progress bar
19999  * @cfg {Boolean} active animated of the progress bar
20000  * 
20001  * 
20002  * @constructor
20003  * Create a new Progress
20004  * @param {Object} config The config object
20005  */
20006
20007 Roo.bootstrap.Progress = function(config){
20008     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20009 };
20010
20011 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20012     
20013     striped : false,
20014     active: false,
20015     
20016     getAutoCreate : function(){
20017         var cfg = {
20018             tag: 'div',
20019             cls: 'progress'
20020         };
20021         
20022         
20023         if(this.striped){
20024             cfg.cls += ' progress-striped';
20025         }
20026       
20027         if(this.active){
20028             cfg.cls += ' active';
20029         }
20030         
20031         
20032         return cfg;
20033     }
20034    
20035 });
20036
20037  
20038
20039  /*
20040  * - LGPL
20041  *
20042  * ProgressBar
20043  * 
20044  */
20045
20046 /**
20047  * @class Roo.bootstrap.ProgressBar
20048  * @extends Roo.bootstrap.Component
20049  * Bootstrap ProgressBar class
20050  * @cfg {Number} aria_valuenow aria-value now
20051  * @cfg {Number} aria_valuemin aria-value min
20052  * @cfg {Number} aria_valuemax aria-value max
20053  * @cfg {String} label label for the progress bar
20054  * @cfg {String} panel (success | info | warning | danger )
20055  * @cfg {String} role role of the progress bar
20056  * @cfg {String} sr_only text
20057  * 
20058  * 
20059  * @constructor
20060  * Create a new ProgressBar
20061  * @param {Object} config The config object
20062  */
20063
20064 Roo.bootstrap.ProgressBar = function(config){
20065     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20066 };
20067
20068 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20069     
20070     aria_valuenow : 0,
20071     aria_valuemin : 0,
20072     aria_valuemax : 100,
20073     label : false,
20074     panel : false,
20075     role : false,
20076     sr_only: false,
20077     
20078     getAutoCreate : function()
20079     {
20080         
20081         var cfg = {
20082             tag: 'div',
20083             cls: 'progress-bar',
20084             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20085         };
20086         
20087         if(this.sr_only){
20088             cfg.cn = {
20089                 tag: 'span',
20090                 cls: 'sr-only',
20091                 html: this.sr_only
20092             }
20093         }
20094         
20095         if(this.role){
20096             cfg.role = this.role;
20097         }
20098         
20099         if(this.aria_valuenow){
20100             cfg['aria-valuenow'] = this.aria_valuenow;
20101         }
20102         
20103         if(this.aria_valuemin){
20104             cfg['aria-valuemin'] = this.aria_valuemin;
20105         }
20106         
20107         if(this.aria_valuemax){
20108             cfg['aria-valuemax'] = this.aria_valuemax;
20109         }
20110         
20111         if(this.label && !this.sr_only){
20112             cfg.html = this.label;
20113         }
20114         
20115         if(this.panel){
20116             cfg.cls += ' progress-bar-' + this.panel;
20117         }
20118         
20119         return cfg;
20120     },
20121     
20122     update : function(aria_valuenow)
20123     {
20124         this.aria_valuenow = aria_valuenow;
20125         
20126         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20127     }
20128    
20129 });
20130
20131  
20132
20133  /*
20134  * - LGPL
20135  *
20136  * column
20137  * 
20138  */
20139
20140 /**
20141  * @class Roo.bootstrap.TabGroup
20142  * @extends Roo.bootstrap.Column
20143  * Bootstrap Column class
20144  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20145  * @cfg {Boolean} carousel true to make the group behave like a carousel
20146  * @cfg {Boolean} bullets show bullets for the panels
20147  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20148  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20149  * @cfg {Boolean} showarrow (true|false) show arrow default true
20150  * 
20151  * @constructor
20152  * Create a new TabGroup
20153  * @param {Object} config The config object
20154  */
20155
20156 Roo.bootstrap.TabGroup = function(config){
20157     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20158     if (!this.navId) {
20159         this.navId = Roo.id();
20160     }
20161     this.tabs = [];
20162     Roo.bootstrap.TabGroup.register(this);
20163     
20164 };
20165
20166 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20167     
20168     carousel : false,
20169     transition : false,
20170     bullets : 0,
20171     timer : 0,
20172     autoslide : false,
20173     slideFn : false,
20174     slideOnTouch : false,
20175     showarrow : true,
20176     
20177     getAutoCreate : function()
20178     {
20179         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20180         
20181         cfg.cls += ' tab-content';
20182         
20183         if (this.carousel) {
20184             cfg.cls += ' carousel slide';
20185             
20186             cfg.cn = [{
20187                cls : 'carousel-inner',
20188                cn : []
20189             }];
20190         
20191             if(this.bullets  && !Roo.isTouch){
20192                 
20193                 var bullets = {
20194                     cls : 'carousel-bullets',
20195                     cn : []
20196                 };
20197                
20198                 if(this.bullets_cls){
20199                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20200                 }
20201                 
20202                 bullets.cn.push({
20203                     cls : 'clear'
20204                 });
20205                 
20206                 cfg.cn[0].cn.push(bullets);
20207             }
20208             
20209             if(this.showarrow){
20210                 cfg.cn[0].cn.push({
20211                     tag : 'div',
20212                     class : 'carousel-arrow',
20213                     cn : [
20214                         {
20215                             tag : 'div',
20216                             class : 'carousel-prev',
20217                             cn : [
20218                                 {
20219                                     tag : 'i',
20220                                     class : 'fa fa-chevron-left'
20221                                 }
20222                             ]
20223                         },
20224                         {
20225                             tag : 'div',
20226                             class : 'carousel-next',
20227                             cn : [
20228                                 {
20229                                     tag : 'i',
20230                                     class : 'fa fa-chevron-right'
20231                                 }
20232                             ]
20233                         }
20234                     ]
20235                 });
20236             }
20237             
20238         }
20239         
20240         return cfg;
20241     },
20242     
20243     initEvents:  function()
20244     {
20245 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20246 //            this.el.on("touchstart", this.onTouchStart, this);
20247 //        }
20248         
20249         if(this.autoslide){
20250             var _this = this;
20251             
20252             this.slideFn = window.setInterval(function() {
20253                 _this.showPanelNext();
20254             }, this.timer);
20255         }
20256         
20257         if(this.showarrow){
20258             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20259             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20260         }
20261         
20262         
20263     },
20264     
20265 //    onTouchStart : function(e, el, o)
20266 //    {
20267 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20268 //            return;
20269 //        }
20270 //        
20271 //        this.showPanelNext();
20272 //    },
20273     
20274     
20275     getChildContainer : function()
20276     {
20277         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20278     },
20279     
20280     /**
20281     * register a Navigation item
20282     * @param {Roo.bootstrap.NavItem} the navitem to add
20283     */
20284     register : function(item)
20285     {
20286         this.tabs.push( item);
20287         item.navId = this.navId; // not really needed..
20288         this.addBullet();
20289     
20290     },
20291     
20292     getActivePanel : function()
20293     {
20294         var r = false;
20295         Roo.each(this.tabs, function(t) {
20296             if (t.active) {
20297                 r = t;
20298                 return false;
20299             }
20300             return null;
20301         });
20302         return r;
20303         
20304     },
20305     getPanelByName : function(n)
20306     {
20307         var r = false;
20308         Roo.each(this.tabs, function(t) {
20309             if (t.tabId == n) {
20310                 r = t;
20311                 return false;
20312             }
20313             return null;
20314         });
20315         return r;
20316     },
20317     indexOfPanel : function(p)
20318     {
20319         var r = false;
20320         Roo.each(this.tabs, function(t,i) {
20321             if (t.tabId == p.tabId) {
20322                 r = i;
20323                 return false;
20324             }
20325             return null;
20326         });
20327         return r;
20328     },
20329     /**
20330      * show a specific panel
20331      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20332      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20333      */
20334     showPanel : function (pan)
20335     {
20336         if(this.transition || typeof(pan) == 'undefined'){
20337             Roo.log("waiting for the transitionend");
20338             return false;
20339         }
20340         
20341         if (typeof(pan) == 'number') {
20342             pan = this.tabs[pan];
20343         }
20344         
20345         if (typeof(pan) == 'string') {
20346             pan = this.getPanelByName(pan);
20347         }
20348         
20349         var cur = this.getActivePanel();
20350         
20351         if(!pan || !cur){
20352             Roo.log('pan or acitve pan is undefined');
20353             return false;
20354         }
20355         
20356         if (pan.tabId == this.getActivePanel().tabId) {
20357             return true;
20358         }
20359         
20360         if (false === cur.fireEvent('beforedeactivate')) {
20361             return false;
20362         }
20363         
20364         if(this.bullets > 0 && !Roo.isTouch){
20365             this.setActiveBullet(this.indexOfPanel(pan));
20366         }
20367         
20368         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20369             
20370             //class="carousel-item carousel-item-next carousel-item-left"
20371             
20372             this.transition = true;
20373             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20374             var lr = dir == 'next' ? 'left' : 'right';
20375             pan.el.addClass(dir); // or prev
20376             pan.el.addClass('carousel-item-' + dir); // or prev
20377             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20378             cur.el.addClass(lr); // or right
20379             pan.el.addClass(lr);
20380             cur.el.addClass('carousel-item-' +lr); // or right
20381             pan.el.addClass('carousel-item-' +lr);
20382             
20383             
20384             var _this = this;
20385             cur.el.on('transitionend', function() {
20386                 Roo.log("trans end?");
20387                 
20388                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20389                 pan.setActive(true);
20390                 
20391                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20392                 cur.setActive(false);
20393                 
20394                 _this.transition = false;
20395                 
20396             }, this, { single:  true } );
20397             
20398             return true;
20399         }
20400         
20401         cur.setActive(false);
20402         pan.setActive(true);
20403         
20404         return true;
20405         
20406     },
20407     showPanelNext : function()
20408     {
20409         var i = this.indexOfPanel(this.getActivePanel());
20410         
20411         if (i >= this.tabs.length - 1 && !this.autoslide) {
20412             return;
20413         }
20414         
20415         if (i >= this.tabs.length - 1 && this.autoslide) {
20416             i = -1;
20417         }
20418         
20419         this.showPanel(this.tabs[i+1]);
20420     },
20421     
20422     showPanelPrev : function()
20423     {
20424         var i = this.indexOfPanel(this.getActivePanel());
20425         
20426         if (i  < 1 && !this.autoslide) {
20427             return;
20428         }
20429         
20430         if (i < 1 && this.autoslide) {
20431             i = this.tabs.length;
20432         }
20433         
20434         this.showPanel(this.tabs[i-1]);
20435     },
20436     
20437     
20438     addBullet: function()
20439     {
20440         if(!this.bullets || Roo.isTouch){
20441             return;
20442         }
20443         var ctr = this.el.select('.carousel-bullets',true).first();
20444         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20445         var bullet = ctr.createChild({
20446             cls : 'bullet bullet-' + i
20447         },ctr.dom.lastChild);
20448         
20449         
20450         var _this = this;
20451         
20452         bullet.on('click', (function(e, el, o, ii, t){
20453
20454             e.preventDefault();
20455
20456             this.showPanel(ii);
20457
20458             if(this.autoslide && this.slideFn){
20459                 clearInterval(this.slideFn);
20460                 this.slideFn = window.setInterval(function() {
20461                     _this.showPanelNext();
20462                 }, this.timer);
20463             }
20464
20465         }).createDelegate(this, [i, bullet], true));
20466                 
20467         
20468     },
20469      
20470     setActiveBullet : function(i)
20471     {
20472         if(Roo.isTouch){
20473             return;
20474         }
20475         
20476         Roo.each(this.el.select('.bullet', true).elements, function(el){
20477             el.removeClass('selected');
20478         });
20479
20480         var bullet = this.el.select('.bullet-' + i, true).first();
20481         
20482         if(!bullet){
20483             return;
20484         }
20485         
20486         bullet.addClass('selected');
20487     }
20488     
20489     
20490   
20491 });
20492
20493  
20494
20495  
20496  
20497 Roo.apply(Roo.bootstrap.TabGroup, {
20498     
20499     groups: {},
20500      /**
20501     * register a Navigation Group
20502     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20503     */
20504     register : function(navgrp)
20505     {
20506         this.groups[navgrp.navId] = navgrp;
20507         
20508     },
20509     /**
20510     * fetch a Navigation Group based on the navigation ID
20511     * if one does not exist , it will get created.
20512     * @param {string} the navgroup to add
20513     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20514     */
20515     get: function(navId) {
20516         if (typeof(this.groups[navId]) == 'undefined') {
20517             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20518         }
20519         return this.groups[navId] ;
20520     }
20521     
20522     
20523     
20524 });
20525
20526  /*
20527  * - LGPL
20528  *
20529  * TabPanel
20530  * 
20531  */
20532
20533 /**
20534  * @class Roo.bootstrap.TabPanel
20535  * @extends Roo.bootstrap.Component
20536  * Bootstrap TabPanel class
20537  * @cfg {Boolean} active panel active
20538  * @cfg {String} html panel content
20539  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20540  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20541  * @cfg {String} href click to link..
20542  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20543  * 
20544  * 
20545  * @constructor
20546  * Create a new TabPanel
20547  * @param {Object} config The config object
20548  */
20549
20550 Roo.bootstrap.TabPanel = function(config){
20551     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20552     this.addEvents({
20553         /**
20554              * @event changed
20555              * Fires when the active status changes
20556              * @param {Roo.bootstrap.TabPanel} this
20557              * @param {Boolean} state the new state
20558             
20559          */
20560         'changed': true,
20561         /**
20562              * @event beforedeactivate
20563              * Fires before a tab is de-activated - can be used to do validation on a form.
20564              * @param {Roo.bootstrap.TabPanel} this
20565              * @return {Boolean} false if there is an error
20566             
20567          */
20568         'beforedeactivate': true
20569      });
20570     
20571     this.tabId = this.tabId || Roo.id();
20572   
20573 };
20574
20575 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20576     
20577     active: false,
20578     html: false,
20579     tabId: false,
20580     navId : false,
20581     href : '',
20582     touchSlide : false,
20583     getAutoCreate : function(){
20584         
20585         
20586         var cfg = {
20587             tag: 'div',
20588             // item is needed for carousel - not sure if it has any effect otherwise
20589             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20590             html: this.html || ''
20591         };
20592         
20593         if(this.active){
20594             cfg.cls += ' active';
20595         }
20596         
20597         if(this.tabId){
20598             cfg.tabId = this.tabId;
20599         }
20600         
20601         
20602         
20603         return cfg;
20604     },
20605     
20606     initEvents:  function()
20607     {
20608         var p = this.parent();
20609         
20610         this.navId = this.navId || p.navId;
20611         
20612         if (typeof(this.navId) != 'undefined') {
20613             // not really needed.. but just in case.. parent should be a NavGroup.
20614             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20615             
20616             tg.register(this);
20617             
20618             var i = tg.tabs.length - 1;
20619             
20620             if(this.active && tg.bullets > 0 && i < tg.bullets){
20621                 tg.setActiveBullet(i);
20622             }
20623         }
20624         
20625         this.el.on('click', this.onClick, this);
20626         
20627         if(Roo.isTouch && this.touchSlide){
20628             this.el.on("touchstart", this.onTouchStart, this);
20629             this.el.on("touchmove", this.onTouchMove, this);
20630             this.el.on("touchend", this.onTouchEnd, this);
20631         }
20632         
20633     },
20634     
20635     onRender : function(ct, position)
20636     {
20637         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20638     },
20639     
20640     setActive : function(state)
20641     {
20642         Roo.log("panel - set active " + this.tabId + "=" + state);
20643         
20644         this.active = state;
20645         if (!state) {
20646             this.el.removeClass('active');
20647             
20648         } else  if (!this.el.hasClass('active')) {
20649             this.el.addClass('active');
20650         }
20651         
20652         this.fireEvent('changed', this, state);
20653     },
20654     
20655     onClick : function(e)
20656     {
20657         e.preventDefault();
20658         
20659         if(!this.href.length){
20660             return;
20661         }
20662         
20663         window.location.href = this.href;
20664     },
20665     
20666     startX : 0,
20667     startY : 0,
20668     endX : 0,
20669     endY : 0,
20670     swiping : false,
20671     
20672     onTouchStart : function(e)
20673     {
20674         this.swiping = false;
20675         
20676         this.startX = e.browserEvent.touches[0].clientX;
20677         this.startY = e.browserEvent.touches[0].clientY;
20678     },
20679     
20680     onTouchMove : function(e)
20681     {
20682         this.swiping = true;
20683         
20684         this.endX = e.browserEvent.touches[0].clientX;
20685         this.endY = e.browserEvent.touches[0].clientY;
20686     },
20687     
20688     onTouchEnd : function(e)
20689     {
20690         if(!this.swiping){
20691             this.onClick(e);
20692             return;
20693         }
20694         
20695         var tabGroup = this.parent();
20696         
20697         if(this.endX > this.startX){ // swiping right
20698             tabGroup.showPanelPrev();
20699             return;
20700         }
20701         
20702         if(this.startX > this.endX){ // swiping left
20703             tabGroup.showPanelNext();
20704             return;
20705         }
20706     }
20707     
20708     
20709 });
20710  
20711
20712  
20713
20714  /*
20715  * - LGPL
20716  *
20717  * DateField
20718  * 
20719  */
20720
20721 /**
20722  * @class Roo.bootstrap.DateField
20723  * @extends Roo.bootstrap.Input
20724  * Bootstrap DateField class
20725  * @cfg {Number} weekStart default 0
20726  * @cfg {String} viewMode default empty, (months|years)
20727  * @cfg {String} minViewMode default empty, (months|years)
20728  * @cfg {Number} startDate default -Infinity
20729  * @cfg {Number} endDate default Infinity
20730  * @cfg {Boolean} todayHighlight default false
20731  * @cfg {Boolean} todayBtn default false
20732  * @cfg {Boolean} calendarWeeks default false
20733  * @cfg {Object} daysOfWeekDisabled default empty
20734  * @cfg {Boolean} singleMode default false (true | false)
20735  * 
20736  * @cfg {Boolean} keyboardNavigation default true
20737  * @cfg {String} language default en
20738  * 
20739  * @constructor
20740  * Create a new DateField
20741  * @param {Object} config The config object
20742  */
20743
20744 Roo.bootstrap.DateField = function(config){
20745     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20746      this.addEvents({
20747             /**
20748              * @event show
20749              * Fires when this field show.
20750              * @param {Roo.bootstrap.DateField} this
20751              * @param {Mixed} date The date value
20752              */
20753             show : true,
20754             /**
20755              * @event show
20756              * Fires when this field hide.
20757              * @param {Roo.bootstrap.DateField} this
20758              * @param {Mixed} date The date value
20759              */
20760             hide : true,
20761             /**
20762              * @event select
20763              * Fires when select a date.
20764              * @param {Roo.bootstrap.DateField} this
20765              * @param {Mixed} date The date value
20766              */
20767             select : true,
20768             /**
20769              * @event beforeselect
20770              * Fires when before select a date.
20771              * @param {Roo.bootstrap.DateField} this
20772              * @param {Mixed} date The date value
20773              */
20774             beforeselect : true
20775         });
20776 };
20777
20778 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20779     
20780     /**
20781      * @cfg {String} format
20782      * The default date format string which can be overriden for localization support.  The format must be
20783      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20784      */
20785     format : "m/d/y",
20786     /**
20787      * @cfg {String} altFormats
20788      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20789      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20790      */
20791     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20792     
20793     weekStart : 0,
20794     
20795     viewMode : '',
20796     
20797     minViewMode : '',
20798     
20799     todayHighlight : false,
20800     
20801     todayBtn: false,
20802     
20803     language: 'en',
20804     
20805     keyboardNavigation: true,
20806     
20807     calendarWeeks: false,
20808     
20809     startDate: -Infinity,
20810     
20811     endDate: Infinity,
20812     
20813     daysOfWeekDisabled: [],
20814     
20815     _events: [],
20816     
20817     singleMode : false,
20818     
20819     UTCDate: function()
20820     {
20821         return new Date(Date.UTC.apply(Date, arguments));
20822     },
20823     
20824     UTCToday: function()
20825     {
20826         var today = new Date();
20827         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20828     },
20829     
20830     getDate: function() {
20831             var d = this.getUTCDate();
20832             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20833     },
20834     
20835     getUTCDate: function() {
20836             return this.date;
20837     },
20838     
20839     setDate: function(d) {
20840             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20841     },
20842     
20843     setUTCDate: function(d) {
20844             this.date = d;
20845             this.setValue(this.formatDate(this.date));
20846     },
20847         
20848     onRender: function(ct, position)
20849     {
20850         
20851         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20852         
20853         this.language = this.language || 'en';
20854         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20855         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20856         
20857         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20858         this.format = this.format || 'm/d/y';
20859         this.isInline = false;
20860         this.isInput = true;
20861         this.component = this.el.select('.add-on', true).first() || false;
20862         this.component = (this.component && this.component.length === 0) ? false : this.component;
20863         this.hasInput = this.component && this.inputEl().length;
20864         
20865         if (typeof(this.minViewMode === 'string')) {
20866             switch (this.minViewMode) {
20867                 case 'months':
20868                     this.minViewMode = 1;
20869                     break;
20870                 case 'years':
20871                     this.minViewMode = 2;
20872                     break;
20873                 default:
20874                     this.minViewMode = 0;
20875                     break;
20876             }
20877         }
20878         
20879         if (typeof(this.viewMode === 'string')) {
20880             switch (this.viewMode) {
20881                 case 'months':
20882                     this.viewMode = 1;
20883                     break;
20884                 case 'years':
20885                     this.viewMode = 2;
20886                     break;
20887                 default:
20888                     this.viewMode = 0;
20889                     break;
20890             }
20891         }
20892                 
20893         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20894         
20895 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20896         
20897         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20898         
20899         this.picker().on('mousedown', this.onMousedown, this);
20900         this.picker().on('click', this.onClick, this);
20901         
20902         this.picker().addClass('datepicker-dropdown');
20903         
20904         this.startViewMode = this.viewMode;
20905         
20906         if(this.singleMode){
20907             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20908                 v.setVisibilityMode(Roo.Element.DISPLAY);
20909                 v.hide();
20910             });
20911             
20912             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20913                 v.setStyle('width', '189px');
20914             });
20915         }
20916         
20917         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20918             if(!this.calendarWeeks){
20919                 v.remove();
20920                 return;
20921             }
20922             
20923             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20924             v.attr('colspan', function(i, val){
20925                 return parseInt(val) + 1;
20926             });
20927         });
20928                         
20929         
20930         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20931         
20932         this.setStartDate(this.startDate);
20933         this.setEndDate(this.endDate);
20934         
20935         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20936         
20937         this.fillDow();
20938         this.fillMonths();
20939         this.update();
20940         this.showMode();
20941         
20942         if(this.isInline) {
20943             this.showPopup();
20944         }
20945     },
20946     
20947     picker : function()
20948     {
20949         return this.pickerEl;
20950 //        return this.el.select('.datepicker', true).first();
20951     },
20952     
20953     fillDow: function()
20954     {
20955         var dowCnt = this.weekStart;
20956         
20957         var dow = {
20958             tag: 'tr',
20959             cn: [
20960                 
20961             ]
20962         };
20963         
20964         if(this.calendarWeeks){
20965             dow.cn.push({
20966                 tag: 'th',
20967                 cls: 'cw',
20968                 html: '&nbsp;'
20969             })
20970         }
20971         
20972         while (dowCnt < this.weekStart + 7) {
20973             dow.cn.push({
20974                 tag: 'th',
20975                 cls: 'dow',
20976                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20977             });
20978         }
20979         
20980         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20981     },
20982     
20983     fillMonths: function()
20984     {    
20985         var i = 0;
20986         var months = this.picker().select('>.datepicker-months td', true).first();
20987         
20988         months.dom.innerHTML = '';
20989         
20990         while (i < 12) {
20991             var month = {
20992                 tag: 'span',
20993                 cls: 'month',
20994                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20995             };
20996             
20997             months.createChild(month);
20998         }
20999         
21000     },
21001     
21002     update: function()
21003     {
21004         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;
21005         
21006         if (this.date < this.startDate) {
21007             this.viewDate = new Date(this.startDate);
21008         } else if (this.date > this.endDate) {
21009             this.viewDate = new Date(this.endDate);
21010         } else {
21011             this.viewDate = new Date(this.date);
21012         }
21013         
21014         this.fill();
21015     },
21016     
21017     fill: function() 
21018     {
21019         var d = new Date(this.viewDate),
21020                 year = d.getUTCFullYear(),
21021                 month = d.getUTCMonth(),
21022                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21023                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21024                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21025                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21026                 currentDate = this.date && this.date.valueOf(),
21027                 today = this.UTCToday();
21028         
21029         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21030         
21031 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21032         
21033 //        this.picker.select('>tfoot th.today').
21034 //                                              .text(dates[this.language].today)
21035 //                                              .toggle(this.todayBtn !== false);
21036     
21037         this.updateNavArrows();
21038         this.fillMonths();
21039                                                 
21040         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21041         
21042         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21043          
21044         prevMonth.setUTCDate(day);
21045         
21046         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21047         
21048         var nextMonth = new Date(prevMonth);
21049         
21050         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21051         
21052         nextMonth = nextMonth.valueOf();
21053         
21054         var fillMonths = false;
21055         
21056         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21057         
21058         while(prevMonth.valueOf() <= nextMonth) {
21059             var clsName = '';
21060             
21061             if (prevMonth.getUTCDay() === this.weekStart) {
21062                 if(fillMonths){
21063                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21064                 }
21065                     
21066                 fillMonths = {
21067                     tag: 'tr',
21068                     cn: []
21069                 };
21070                 
21071                 if(this.calendarWeeks){
21072                     // ISO 8601: First week contains first thursday.
21073                     // ISO also states week starts on Monday, but we can be more abstract here.
21074                     var
21075                     // Start of current week: based on weekstart/current date
21076                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21077                     // Thursday of this week
21078                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21079                     // First Thursday of year, year from thursday
21080                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21081                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21082                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21083                     
21084                     fillMonths.cn.push({
21085                         tag: 'td',
21086                         cls: 'cw',
21087                         html: calWeek
21088                     });
21089                 }
21090             }
21091             
21092             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21093                 clsName += ' old';
21094             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21095                 clsName += ' new';
21096             }
21097             if (this.todayHighlight &&
21098                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21099                 prevMonth.getUTCMonth() == today.getMonth() &&
21100                 prevMonth.getUTCDate() == today.getDate()) {
21101                 clsName += ' today';
21102             }
21103             
21104             if (currentDate && prevMonth.valueOf() === currentDate) {
21105                 clsName += ' active';
21106             }
21107             
21108             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21109                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21110                     clsName += ' disabled';
21111             }
21112             
21113             fillMonths.cn.push({
21114                 tag: 'td',
21115                 cls: 'day ' + clsName,
21116                 html: prevMonth.getDate()
21117             });
21118             
21119             prevMonth.setDate(prevMonth.getDate()+1);
21120         }
21121           
21122         var currentYear = this.date && this.date.getUTCFullYear();
21123         var currentMonth = this.date && this.date.getUTCMonth();
21124         
21125         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21126         
21127         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21128             v.removeClass('active');
21129             
21130             if(currentYear === year && k === currentMonth){
21131                 v.addClass('active');
21132             }
21133             
21134             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21135                 v.addClass('disabled');
21136             }
21137             
21138         });
21139         
21140         
21141         year = parseInt(year/10, 10) * 10;
21142         
21143         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21144         
21145         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21146         
21147         year -= 1;
21148         for (var i = -1; i < 11; i++) {
21149             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21150                 tag: 'span',
21151                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21152                 html: year
21153             });
21154             
21155             year += 1;
21156         }
21157     },
21158     
21159     showMode: function(dir) 
21160     {
21161         if (dir) {
21162             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21163         }
21164         
21165         Roo.each(this.picker().select('>div',true).elements, function(v){
21166             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21167             v.hide();
21168         });
21169         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21170     },
21171     
21172     place: function()
21173     {
21174         if(this.isInline) {
21175             return;
21176         }
21177         
21178         this.picker().removeClass(['bottom', 'top']);
21179         
21180         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21181             /*
21182              * place to the top of element!
21183              *
21184              */
21185             
21186             this.picker().addClass('top');
21187             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21188             
21189             return;
21190         }
21191         
21192         this.picker().addClass('bottom');
21193         
21194         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21195     },
21196     
21197     parseDate : function(value)
21198     {
21199         if(!value || value instanceof Date){
21200             return value;
21201         }
21202         var v = Date.parseDate(value, this.format);
21203         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21204             v = Date.parseDate(value, 'Y-m-d');
21205         }
21206         if(!v && this.altFormats){
21207             if(!this.altFormatsArray){
21208                 this.altFormatsArray = this.altFormats.split("|");
21209             }
21210             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21211                 v = Date.parseDate(value, this.altFormatsArray[i]);
21212             }
21213         }
21214         return v;
21215     },
21216     
21217     formatDate : function(date, fmt)
21218     {   
21219         return (!date || !(date instanceof Date)) ?
21220         date : date.dateFormat(fmt || this.format);
21221     },
21222     
21223     onFocus : function()
21224     {
21225         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21226         this.showPopup();
21227     },
21228     
21229     onBlur : function()
21230     {
21231         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21232         
21233         var d = this.inputEl().getValue();
21234         
21235         this.setValue(d);
21236                 
21237         this.hidePopup();
21238     },
21239     
21240     showPopup : function()
21241     {
21242         this.picker().show();
21243         this.update();
21244         this.place();
21245         
21246         this.fireEvent('showpopup', this, this.date);
21247     },
21248     
21249     hidePopup : function()
21250     {
21251         if(this.isInline) {
21252             return;
21253         }
21254         this.picker().hide();
21255         this.viewMode = this.startViewMode;
21256         this.showMode();
21257         
21258         this.fireEvent('hidepopup', this, this.date);
21259         
21260     },
21261     
21262     onMousedown: function(e)
21263     {
21264         e.stopPropagation();
21265         e.preventDefault();
21266     },
21267     
21268     keyup: function(e)
21269     {
21270         Roo.bootstrap.DateField.superclass.keyup.call(this);
21271         this.update();
21272     },
21273
21274     setValue: function(v)
21275     {
21276         if(this.fireEvent('beforeselect', this, v) !== false){
21277             var d = new Date(this.parseDate(v) ).clearTime();
21278         
21279             if(isNaN(d.getTime())){
21280                 this.date = this.viewDate = '';
21281                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21282                 return;
21283             }
21284
21285             v = this.formatDate(d);
21286
21287             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21288
21289             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21290
21291             this.update();
21292
21293             this.fireEvent('select', this, this.date);
21294         }
21295     },
21296     
21297     getValue: function()
21298     {
21299         return this.formatDate(this.date);
21300     },
21301     
21302     fireKey: function(e)
21303     {
21304         if (!this.picker().isVisible()){
21305             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21306                 this.showPopup();
21307             }
21308             return;
21309         }
21310         
21311         var dateChanged = false,
21312         dir, day, month,
21313         newDate, newViewDate;
21314         
21315         switch(e.keyCode){
21316             case 27: // escape
21317                 this.hidePopup();
21318                 e.preventDefault();
21319                 break;
21320             case 37: // left
21321             case 39: // right
21322                 if (!this.keyboardNavigation) {
21323                     break;
21324                 }
21325                 dir = e.keyCode == 37 ? -1 : 1;
21326                 
21327                 if (e.ctrlKey){
21328                     newDate = this.moveYear(this.date, dir);
21329                     newViewDate = this.moveYear(this.viewDate, dir);
21330                 } else if (e.shiftKey){
21331                     newDate = this.moveMonth(this.date, dir);
21332                     newViewDate = this.moveMonth(this.viewDate, dir);
21333                 } else {
21334                     newDate = new Date(this.date);
21335                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21336                     newViewDate = new Date(this.viewDate);
21337                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21338                 }
21339                 if (this.dateWithinRange(newDate)){
21340                     this.date = newDate;
21341                     this.viewDate = newViewDate;
21342                     this.setValue(this.formatDate(this.date));
21343 //                    this.update();
21344                     e.preventDefault();
21345                     dateChanged = true;
21346                 }
21347                 break;
21348             case 38: // up
21349             case 40: // down
21350                 if (!this.keyboardNavigation) {
21351                     break;
21352                 }
21353                 dir = e.keyCode == 38 ? -1 : 1;
21354                 if (e.ctrlKey){
21355                     newDate = this.moveYear(this.date, dir);
21356                     newViewDate = this.moveYear(this.viewDate, dir);
21357                 } else if (e.shiftKey){
21358                     newDate = this.moveMonth(this.date, dir);
21359                     newViewDate = this.moveMonth(this.viewDate, dir);
21360                 } else {
21361                     newDate = new Date(this.date);
21362                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21363                     newViewDate = new Date(this.viewDate);
21364                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21365                 }
21366                 if (this.dateWithinRange(newDate)){
21367                     this.date = newDate;
21368                     this.viewDate = newViewDate;
21369                     this.setValue(this.formatDate(this.date));
21370 //                    this.update();
21371                     e.preventDefault();
21372                     dateChanged = true;
21373                 }
21374                 break;
21375             case 13: // enter
21376                 this.setValue(this.formatDate(this.date));
21377                 this.hidePopup();
21378                 e.preventDefault();
21379                 break;
21380             case 9: // tab
21381                 this.setValue(this.formatDate(this.date));
21382                 this.hidePopup();
21383                 break;
21384             case 16: // shift
21385             case 17: // ctrl
21386             case 18: // alt
21387                 break;
21388             default :
21389                 this.hidePopup();
21390                 
21391         }
21392     },
21393     
21394     
21395     onClick: function(e) 
21396     {
21397         e.stopPropagation();
21398         e.preventDefault();
21399         
21400         var target = e.getTarget();
21401         
21402         if(target.nodeName.toLowerCase() === 'i'){
21403             target = Roo.get(target).dom.parentNode;
21404         }
21405         
21406         var nodeName = target.nodeName;
21407         var className = target.className;
21408         var html = target.innerHTML;
21409         //Roo.log(nodeName);
21410         
21411         switch(nodeName.toLowerCase()) {
21412             case 'th':
21413                 switch(className) {
21414                     case 'switch':
21415                         this.showMode(1);
21416                         break;
21417                     case 'prev':
21418                     case 'next':
21419                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21420                         switch(this.viewMode){
21421                                 case 0:
21422                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21423                                         break;
21424                                 case 1:
21425                                 case 2:
21426                                         this.viewDate = this.moveYear(this.viewDate, dir);
21427                                         break;
21428                         }
21429                         this.fill();
21430                         break;
21431                     case 'today':
21432                         var date = new Date();
21433                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21434 //                        this.fill()
21435                         this.setValue(this.formatDate(this.date));
21436                         
21437                         this.hidePopup();
21438                         break;
21439                 }
21440                 break;
21441             case 'span':
21442                 if (className.indexOf('disabled') < 0) {
21443                     this.viewDate.setUTCDate(1);
21444                     if (className.indexOf('month') > -1) {
21445                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21446                     } else {
21447                         var year = parseInt(html, 10) || 0;
21448                         this.viewDate.setUTCFullYear(year);
21449                         
21450                     }
21451                     
21452                     if(this.singleMode){
21453                         this.setValue(this.formatDate(this.viewDate));
21454                         this.hidePopup();
21455                         return;
21456                     }
21457                     
21458                     this.showMode(-1);
21459                     this.fill();
21460                 }
21461                 break;
21462                 
21463             case 'td':
21464                 //Roo.log(className);
21465                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21466                     var day = parseInt(html, 10) || 1;
21467                     var year = this.viewDate.getUTCFullYear(),
21468                         month = this.viewDate.getUTCMonth();
21469
21470                     if (className.indexOf('old') > -1) {
21471                         if(month === 0 ){
21472                             month = 11;
21473                             year -= 1;
21474                         }else{
21475                             month -= 1;
21476                         }
21477                     } else if (className.indexOf('new') > -1) {
21478                         if (month == 11) {
21479                             month = 0;
21480                             year += 1;
21481                         } else {
21482                             month += 1;
21483                         }
21484                     }
21485                     //Roo.log([year,month,day]);
21486                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21487                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21488 //                    this.fill();
21489                     //Roo.log(this.formatDate(this.date));
21490                     this.setValue(this.formatDate(this.date));
21491                     this.hidePopup();
21492                 }
21493                 break;
21494         }
21495     },
21496     
21497     setStartDate: function(startDate)
21498     {
21499         this.startDate = startDate || -Infinity;
21500         if (this.startDate !== -Infinity) {
21501             this.startDate = this.parseDate(this.startDate);
21502         }
21503         this.update();
21504         this.updateNavArrows();
21505     },
21506
21507     setEndDate: function(endDate)
21508     {
21509         this.endDate = endDate || Infinity;
21510         if (this.endDate !== Infinity) {
21511             this.endDate = this.parseDate(this.endDate);
21512         }
21513         this.update();
21514         this.updateNavArrows();
21515     },
21516     
21517     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21518     {
21519         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21520         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21521             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21522         }
21523         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21524             return parseInt(d, 10);
21525         });
21526         this.update();
21527         this.updateNavArrows();
21528     },
21529     
21530     updateNavArrows: function() 
21531     {
21532         if(this.singleMode){
21533             return;
21534         }
21535         
21536         var d = new Date(this.viewDate),
21537         year = d.getUTCFullYear(),
21538         month = d.getUTCMonth();
21539         
21540         Roo.each(this.picker().select('.prev', true).elements, function(v){
21541             v.show();
21542             switch (this.viewMode) {
21543                 case 0:
21544
21545                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21546                         v.hide();
21547                     }
21548                     break;
21549                 case 1:
21550                 case 2:
21551                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21552                         v.hide();
21553                     }
21554                     break;
21555             }
21556         });
21557         
21558         Roo.each(this.picker().select('.next', true).elements, function(v){
21559             v.show();
21560             switch (this.viewMode) {
21561                 case 0:
21562
21563                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21564                         v.hide();
21565                     }
21566                     break;
21567                 case 1:
21568                 case 2:
21569                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21570                         v.hide();
21571                     }
21572                     break;
21573             }
21574         })
21575     },
21576     
21577     moveMonth: function(date, dir)
21578     {
21579         if (!dir) {
21580             return date;
21581         }
21582         var new_date = new Date(date.valueOf()),
21583         day = new_date.getUTCDate(),
21584         month = new_date.getUTCMonth(),
21585         mag = Math.abs(dir),
21586         new_month, test;
21587         dir = dir > 0 ? 1 : -1;
21588         if (mag == 1){
21589             test = dir == -1
21590             // If going back one month, make sure month is not current month
21591             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21592             ? function(){
21593                 return new_date.getUTCMonth() == month;
21594             }
21595             // If going forward one month, make sure month is as expected
21596             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21597             : function(){
21598                 return new_date.getUTCMonth() != new_month;
21599             };
21600             new_month = month + dir;
21601             new_date.setUTCMonth(new_month);
21602             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21603             if (new_month < 0 || new_month > 11) {
21604                 new_month = (new_month + 12) % 12;
21605             }
21606         } else {
21607             // For magnitudes >1, move one month at a time...
21608             for (var i=0; i<mag; i++) {
21609                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21610                 new_date = this.moveMonth(new_date, dir);
21611             }
21612             // ...then reset the day, keeping it in the new month
21613             new_month = new_date.getUTCMonth();
21614             new_date.setUTCDate(day);
21615             test = function(){
21616                 return new_month != new_date.getUTCMonth();
21617             };
21618         }
21619         // Common date-resetting loop -- if date is beyond end of month, make it
21620         // end of month
21621         while (test()){
21622             new_date.setUTCDate(--day);
21623             new_date.setUTCMonth(new_month);
21624         }
21625         return new_date;
21626     },
21627
21628     moveYear: function(date, dir)
21629     {
21630         return this.moveMonth(date, dir*12);
21631     },
21632
21633     dateWithinRange: function(date)
21634     {
21635         return date >= this.startDate && date <= this.endDate;
21636     },
21637
21638     
21639     remove: function() 
21640     {
21641         this.picker().remove();
21642     },
21643     
21644     validateValue : function(value)
21645     {
21646         if(this.getVisibilityEl().hasClass('hidden')){
21647             return true;
21648         }
21649         
21650         if(value.length < 1)  {
21651             if(this.allowBlank){
21652                 return true;
21653             }
21654             return false;
21655         }
21656         
21657         if(value.length < this.minLength){
21658             return false;
21659         }
21660         if(value.length > this.maxLength){
21661             return false;
21662         }
21663         if(this.vtype){
21664             var vt = Roo.form.VTypes;
21665             if(!vt[this.vtype](value, this)){
21666                 return false;
21667             }
21668         }
21669         if(typeof this.validator == "function"){
21670             var msg = this.validator(value);
21671             if(msg !== true){
21672                 return false;
21673             }
21674         }
21675         
21676         if(this.regex && !this.regex.test(value)){
21677             return false;
21678         }
21679         
21680         if(typeof(this.parseDate(value)) == 'undefined'){
21681             return false;
21682         }
21683         
21684         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21685             return false;
21686         }      
21687         
21688         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21689             return false;
21690         } 
21691         
21692         
21693         return true;
21694     },
21695     
21696     reset : function()
21697     {
21698         this.date = this.viewDate = '';
21699         
21700         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21701     }
21702    
21703 });
21704
21705 Roo.apply(Roo.bootstrap.DateField,  {
21706     
21707     head : {
21708         tag: 'thead',
21709         cn: [
21710         {
21711             tag: 'tr',
21712             cn: [
21713             {
21714                 tag: 'th',
21715                 cls: 'prev',
21716                 html: '<i class="fa fa-arrow-left"/>'
21717             },
21718             {
21719                 tag: 'th',
21720                 cls: 'switch',
21721                 colspan: '5'
21722             },
21723             {
21724                 tag: 'th',
21725                 cls: 'next',
21726                 html: '<i class="fa fa-arrow-right"/>'
21727             }
21728
21729             ]
21730         }
21731         ]
21732     },
21733     
21734     content : {
21735         tag: 'tbody',
21736         cn: [
21737         {
21738             tag: 'tr',
21739             cn: [
21740             {
21741                 tag: 'td',
21742                 colspan: '7'
21743             }
21744             ]
21745         }
21746         ]
21747     },
21748     
21749     footer : {
21750         tag: 'tfoot',
21751         cn: [
21752         {
21753             tag: 'tr',
21754             cn: [
21755             {
21756                 tag: 'th',
21757                 colspan: '7',
21758                 cls: 'today'
21759             }
21760                     
21761             ]
21762         }
21763         ]
21764     },
21765     
21766     dates:{
21767         en: {
21768             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21769             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21770             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21771             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21772             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21773             today: "Today"
21774         }
21775     },
21776     
21777     modes: [
21778     {
21779         clsName: 'days',
21780         navFnc: 'Month',
21781         navStep: 1
21782     },
21783     {
21784         clsName: 'months',
21785         navFnc: 'FullYear',
21786         navStep: 1
21787     },
21788     {
21789         clsName: 'years',
21790         navFnc: 'FullYear',
21791         navStep: 10
21792     }]
21793 });
21794
21795 Roo.apply(Roo.bootstrap.DateField,  {
21796   
21797     template : {
21798         tag: 'div',
21799         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21800         cn: [
21801         {
21802             tag: 'div',
21803             cls: 'datepicker-days',
21804             cn: [
21805             {
21806                 tag: 'table',
21807                 cls: 'table-condensed',
21808                 cn:[
21809                 Roo.bootstrap.DateField.head,
21810                 {
21811                     tag: 'tbody'
21812                 },
21813                 Roo.bootstrap.DateField.footer
21814                 ]
21815             }
21816             ]
21817         },
21818         {
21819             tag: 'div',
21820             cls: 'datepicker-months',
21821             cn: [
21822             {
21823                 tag: 'table',
21824                 cls: 'table-condensed',
21825                 cn:[
21826                 Roo.bootstrap.DateField.head,
21827                 Roo.bootstrap.DateField.content,
21828                 Roo.bootstrap.DateField.footer
21829                 ]
21830             }
21831             ]
21832         },
21833         {
21834             tag: 'div',
21835             cls: 'datepicker-years',
21836             cn: [
21837             {
21838                 tag: 'table',
21839                 cls: 'table-condensed',
21840                 cn:[
21841                 Roo.bootstrap.DateField.head,
21842                 Roo.bootstrap.DateField.content,
21843                 Roo.bootstrap.DateField.footer
21844                 ]
21845             }
21846             ]
21847         }
21848         ]
21849     }
21850 });
21851
21852  
21853
21854  /*
21855  * - LGPL
21856  *
21857  * TimeField
21858  * 
21859  */
21860
21861 /**
21862  * @class Roo.bootstrap.TimeField
21863  * @extends Roo.bootstrap.Input
21864  * Bootstrap DateField class
21865  * 
21866  * 
21867  * @constructor
21868  * Create a new TimeField
21869  * @param {Object} config The config object
21870  */
21871
21872 Roo.bootstrap.TimeField = function(config){
21873     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21874     this.addEvents({
21875             /**
21876              * @event show
21877              * Fires when this field show.
21878              * @param {Roo.bootstrap.DateField} thisthis
21879              * @param {Mixed} date The date value
21880              */
21881             show : true,
21882             /**
21883              * @event show
21884              * Fires when this field hide.
21885              * @param {Roo.bootstrap.DateField} this
21886              * @param {Mixed} date The date value
21887              */
21888             hide : true,
21889             /**
21890              * @event select
21891              * Fires when select a date.
21892              * @param {Roo.bootstrap.DateField} this
21893              * @param {Mixed} date The date value
21894              */
21895             select : true
21896         });
21897 };
21898
21899 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21900     
21901     /**
21902      * @cfg {String} format
21903      * The default time format string which can be overriden for localization support.  The format must be
21904      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21905      */
21906     format : "H:i",
21907        
21908     onRender: function(ct, position)
21909     {
21910         
21911         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21912                 
21913         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21914         
21915         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21916         
21917         this.pop = this.picker().select('>.datepicker-time',true).first();
21918         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21919         
21920         this.picker().on('mousedown', this.onMousedown, this);
21921         this.picker().on('click', this.onClick, this);
21922         
21923         this.picker().addClass('datepicker-dropdown');
21924     
21925         this.fillTime();
21926         this.update();
21927             
21928         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21929         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21930         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21931         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21932         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21933         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21934
21935     },
21936     
21937     fireKey: function(e){
21938         if (!this.picker().isVisible()){
21939             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21940                 this.show();
21941             }
21942             return;
21943         }
21944
21945         e.preventDefault();
21946         
21947         switch(e.keyCode){
21948             case 27: // escape
21949                 this.hide();
21950                 break;
21951             case 37: // left
21952             case 39: // right
21953                 this.onTogglePeriod();
21954                 break;
21955             case 38: // up
21956                 this.onIncrementMinutes();
21957                 break;
21958             case 40: // down
21959                 this.onDecrementMinutes();
21960                 break;
21961             case 13: // enter
21962             case 9: // tab
21963                 this.setTime();
21964                 break;
21965         }
21966     },
21967     
21968     onClick: function(e) {
21969         e.stopPropagation();
21970         e.preventDefault();
21971     },
21972     
21973     picker : function()
21974     {
21975         return this.el.select('.datepicker', true).first();
21976     },
21977     
21978     fillTime: function()
21979     {    
21980         var time = this.pop.select('tbody', true).first();
21981         
21982         time.dom.innerHTML = '';
21983         
21984         time.createChild({
21985             tag: 'tr',
21986             cn: [
21987                 {
21988                     tag: 'td',
21989                     cn: [
21990                         {
21991                             tag: 'a',
21992                             href: '#',
21993                             cls: 'btn',
21994                             cn: [
21995                                 {
21996                                     tag: 'span',
21997                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
21998                                 }
21999                             ]
22000                         } 
22001                     ]
22002                 },
22003                 {
22004                     tag: 'td',
22005                     cls: 'separator'
22006                 },
22007                 {
22008                     tag: 'td',
22009                     cn: [
22010                         {
22011                             tag: 'a',
22012                             href: '#',
22013                             cls: 'btn',
22014                             cn: [
22015                                 {
22016                                     tag: 'span',
22017                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22018                                 }
22019                             ]
22020                         }
22021                     ]
22022                 },
22023                 {
22024                     tag: 'td',
22025                     cls: 'separator'
22026                 }
22027             ]
22028         });
22029         
22030         time.createChild({
22031             tag: 'tr',
22032             cn: [
22033                 {
22034                     tag: 'td',
22035                     cn: [
22036                         {
22037                             tag: 'span',
22038                             cls: 'timepicker-hour',
22039                             html: '00'
22040                         }  
22041                     ]
22042                 },
22043                 {
22044                     tag: 'td',
22045                     cls: 'separator',
22046                     html: ':'
22047                 },
22048                 {
22049                     tag: 'td',
22050                     cn: [
22051                         {
22052                             tag: 'span',
22053                             cls: 'timepicker-minute',
22054                             html: '00'
22055                         }  
22056                     ]
22057                 },
22058                 {
22059                     tag: 'td',
22060                     cls: 'separator'
22061                 },
22062                 {
22063                     tag: 'td',
22064                     cn: [
22065                         {
22066                             tag: 'button',
22067                             type: 'button',
22068                             cls: 'btn btn-primary period',
22069                             html: 'AM'
22070                             
22071                         }
22072                     ]
22073                 }
22074             ]
22075         });
22076         
22077         time.createChild({
22078             tag: 'tr',
22079             cn: [
22080                 {
22081                     tag: 'td',
22082                     cn: [
22083                         {
22084                             tag: 'a',
22085                             href: '#',
22086                             cls: 'btn',
22087                             cn: [
22088                                 {
22089                                     tag: 'span',
22090                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22091                                 }
22092                             ]
22093                         }
22094                     ]
22095                 },
22096                 {
22097                     tag: 'td',
22098                     cls: 'separator'
22099                 },
22100                 {
22101                     tag: 'td',
22102                     cn: [
22103                         {
22104                             tag: 'a',
22105                             href: '#',
22106                             cls: 'btn',
22107                             cn: [
22108                                 {
22109                                     tag: 'span',
22110                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22111                                 }
22112                             ]
22113                         }
22114                     ]
22115                 },
22116                 {
22117                     tag: 'td',
22118                     cls: 'separator'
22119                 }
22120             ]
22121         });
22122         
22123     },
22124     
22125     update: function()
22126     {
22127         
22128         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22129         
22130         this.fill();
22131     },
22132     
22133     fill: function() 
22134     {
22135         var hours = this.time.getHours();
22136         var minutes = this.time.getMinutes();
22137         var period = 'AM';
22138         
22139         if(hours > 11){
22140             period = 'PM';
22141         }
22142         
22143         if(hours == 0){
22144             hours = 12;
22145         }
22146         
22147         
22148         if(hours > 12){
22149             hours = hours - 12;
22150         }
22151         
22152         if(hours < 10){
22153             hours = '0' + hours;
22154         }
22155         
22156         if(minutes < 10){
22157             minutes = '0' + minutes;
22158         }
22159         
22160         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22161         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22162         this.pop.select('button', true).first().dom.innerHTML = period;
22163         
22164     },
22165     
22166     place: function()
22167     {   
22168         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22169         
22170         var cls = ['bottom'];
22171         
22172         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22173             cls.pop();
22174             cls.push('top');
22175         }
22176         
22177         cls.push('right');
22178         
22179         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22180             cls.pop();
22181             cls.push('left');
22182         }
22183         
22184         this.picker().addClass(cls.join('-'));
22185         
22186         var _this = this;
22187         
22188         Roo.each(cls, function(c){
22189             if(c == 'bottom'){
22190                 _this.picker().setTop(_this.inputEl().getHeight());
22191                 return;
22192             }
22193             if(c == 'top'){
22194                 _this.picker().setTop(0 - _this.picker().getHeight());
22195                 return;
22196             }
22197             
22198             if(c == 'left'){
22199                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22200                 return;
22201             }
22202             if(c == 'right'){
22203                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22204                 return;
22205             }
22206         });
22207         
22208     },
22209   
22210     onFocus : function()
22211     {
22212         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22213         this.show();
22214     },
22215     
22216     onBlur : function()
22217     {
22218         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22219         this.hide();
22220     },
22221     
22222     show : function()
22223     {
22224         this.picker().show();
22225         this.pop.show();
22226         this.update();
22227         this.place();
22228         
22229         this.fireEvent('show', this, this.date);
22230     },
22231     
22232     hide : function()
22233     {
22234         this.picker().hide();
22235         this.pop.hide();
22236         
22237         this.fireEvent('hide', this, this.date);
22238     },
22239     
22240     setTime : function()
22241     {
22242         this.hide();
22243         this.setValue(this.time.format(this.format));
22244         
22245         this.fireEvent('select', this, this.date);
22246         
22247         
22248     },
22249     
22250     onMousedown: function(e){
22251         e.stopPropagation();
22252         e.preventDefault();
22253     },
22254     
22255     onIncrementHours: function()
22256     {
22257         Roo.log('onIncrementHours');
22258         this.time = this.time.add(Date.HOUR, 1);
22259         this.update();
22260         
22261     },
22262     
22263     onDecrementHours: function()
22264     {
22265         Roo.log('onDecrementHours');
22266         this.time = this.time.add(Date.HOUR, -1);
22267         this.update();
22268     },
22269     
22270     onIncrementMinutes: function()
22271     {
22272         Roo.log('onIncrementMinutes');
22273         this.time = this.time.add(Date.MINUTE, 1);
22274         this.update();
22275     },
22276     
22277     onDecrementMinutes: function()
22278     {
22279         Roo.log('onDecrementMinutes');
22280         this.time = this.time.add(Date.MINUTE, -1);
22281         this.update();
22282     },
22283     
22284     onTogglePeriod: function()
22285     {
22286         Roo.log('onTogglePeriod');
22287         this.time = this.time.add(Date.HOUR, 12);
22288         this.update();
22289     }
22290     
22291    
22292 });
22293
22294 Roo.apply(Roo.bootstrap.TimeField,  {
22295     
22296     content : {
22297         tag: 'tbody',
22298         cn: [
22299             {
22300                 tag: 'tr',
22301                 cn: [
22302                 {
22303                     tag: 'td',
22304                     colspan: '7'
22305                 }
22306                 ]
22307             }
22308         ]
22309     },
22310     
22311     footer : {
22312         tag: 'tfoot',
22313         cn: [
22314             {
22315                 tag: 'tr',
22316                 cn: [
22317                 {
22318                     tag: 'th',
22319                     colspan: '7',
22320                     cls: '',
22321                     cn: [
22322                         {
22323                             tag: 'button',
22324                             cls: 'btn btn-info ok',
22325                             html: 'OK'
22326                         }
22327                     ]
22328                 }
22329
22330                 ]
22331             }
22332         ]
22333     }
22334 });
22335
22336 Roo.apply(Roo.bootstrap.TimeField,  {
22337   
22338     template : {
22339         tag: 'div',
22340         cls: 'datepicker dropdown-menu',
22341         cn: [
22342             {
22343                 tag: 'div',
22344                 cls: 'datepicker-time',
22345                 cn: [
22346                 {
22347                     tag: 'table',
22348                     cls: 'table-condensed',
22349                     cn:[
22350                     Roo.bootstrap.TimeField.content,
22351                     Roo.bootstrap.TimeField.footer
22352                     ]
22353                 }
22354                 ]
22355             }
22356         ]
22357     }
22358 });
22359
22360  
22361
22362  /*
22363  * - LGPL
22364  *
22365  * MonthField
22366  * 
22367  */
22368
22369 /**
22370  * @class Roo.bootstrap.MonthField
22371  * @extends Roo.bootstrap.Input
22372  * Bootstrap MonthField class
22373  * 
22374  * @cfg {String} language default en
22375  * 
22376  * @constructor
22377  * Create a new MonthField
22378  * @param {Object} config The config object
22379  */
22380
22381 Roo.bootstrap.MonthField = function(config){
22382     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22383     
22384     this.addEvents({
22385         /**
22386          * @event show
22387          * Fires when this field show.
22388          * @param {Roo.bootstrap.MonthField} this
22389          * @param {Mixed} date The date value
22390          */
22391         show : true,
22392         /**
22393          * @event show
22394          * Fires when this field hide.
22395          * @param {Roo.bootstrap.MonthField} this
22396          * @param {Mixed} date The date value
22397          */
22398         hide : true,
22399         /**
22400          * @event select
22401          * Fires when select a date.
22402          * @param {Roo.bootstrap.MonthField} this
22403          * @param {String} oldvalue The old value
22404          * @param {String} newvalue The new value
22405          */
22406         select : true
22407     });
22408 };
22409
22410 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22411     
22412     onRender: function(ct, position)
22413     {
22414         
22415         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22416         
22417         this.language = this.language || 'en';
22418         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22419         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22420         
22421         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22422         this.isInline = false;
22423         this.isInput = true;
22424         this.component = this.el.select('.add-on', true).first() || false;
22425         this.component = (this.component && this.component.length === 0) ? false : this.component;
22426         this.hasInput = this.component && this.inputEL().length;
22427         
22428         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22429         
22430         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22431         
22432         this.picker().on('mousedown', this.onMousedown, this);
22433         this.picker().on('click', this.onClick, this);
22434         
22435         this.picker().addClass('datepicker-dropdown');
22436         
22437         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22438             v.setStyle('width', '189px');
22439         });
22440         
22441         this.fillMonths();
22442         
22443         this.update();
22444         
22445         if(this.isInline) {
22446             this.show();
22447         }
22448         
22449     },
22450     
22451     setValue: function(v, suppressEvent)
22452     {   
22453         var o = this.getValue();
22454         
22455         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22456         
22457         this.update();
22458
22459         if(suppressEvent !== true){
22460             this.fireEvent('select', this, o, v);
22461         }
22462         
22463     },
22464     
22465     getValue: function()
22466     {
22467         return this.value;
22468     },
22469     
22470     onClick: function(e) 
22471     {
22472         e.stopPropagation();
22473         e.preventDefault();
22474         
22475         var target = e.getTarget();
22476         
22477         if(target.nodeName.toLowerCase() === 'i'){
22478             target = Roo.get(target).dom.parentNode;
22479         }
22480         
22481         var nodeName = target.nodeName;
22482         var className = target.className;
22483         var html = target.innerHTML;
22484         
22485         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22486             return;
22487         }
22488         
22489         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22490         
22491         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22492         
22493         this.hide();
22494                         
22495     },
22496     
22497     picker : function()
22498     {
22499         return this.pickerEl;
22500     },
22501     
22502     fillMonths: function()
22503     {    
22504         var i = 0;
22505         var months = this.picker().select('>.datepicker-months td', true).first();
22506         
22507         months.dom.innerHTML = '';
22508         
22509         while (i < 12) {
22510             var month = {
22511                 tag: 'span',
22512                 cls: 'month',
22513                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22514             };
22515             
22516             months.createChild(month);
22517         }
22518         
22519     },
22520     
22521     update: function()
22522     {
22523         var _this = this;
22524         
22525         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22526             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22527         }
22528         
22529         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22530             e.removeClass('active');
22531             
22532             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22533                 e.addClass('active');
22534             }
22535         })
22536     },
22537     
22538     place: function()
22539     {
22540         if(this.isInline) {
22541             return;
22542         }
22543         
22544         this.picker().removeClass(['bottom', 'top']);
22545         
22546         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22547             /*
22548              * place to the top of element!
22549              *
22550              */
22551             
22552             this.picker().addClass('top');
22553             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22554             
22555             return;
22556         }
22557         
22558         this.picker().addClass('bottom');
22559         
22560         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22561     },
22562     
22563     onFocus : function()
22564     {
22565         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22566         this.show();
22567     },
22568     
22569     onBlur : function()
22570     {
22571         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22572         
22573         var d = this.inputEl().getValue();
22574         
22575         this.setValue(d);
22576                 
22577         this.hide();
22578     },
22579     
22580     show : function()
22581     {
22582         this.picker().show();
22583         this.picker().select('>.datepicker-months', true).first().show();
22584         this.update();
22585         this.place();
22586         
22587         this.fireEvent('show', this, this.date);
22588     },
22589     
22590     hide : function()
22591     {
22592         if(this.isInline) {
22593             return;
22594         }
22595         this.picker().hide();
22596         this.fireEvent('hide', this, this.date);
22597         
22598     },
22599     
22600     onMousedown: function(e)
22601     {
22602         e.stopPropagation();
22603         e.preventDefault();
22604     },
22605     
22606     keyup: function(e)
22607     {
22608         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22609         this.update();
22610     },
22611
22612     fireKey: function(e)
22613     {
22614         if (!this.picker().isVisible()){
22615             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22616                 this.show();
22617             }
22618             return;
22619         }
22620         
22621         var dir;
22622         
22623         switch(e.keyCode){
22624             case 27: // escape
22625                 this.hide();
22626                 e.preventDefault();
22627                 break;
22628             case 37: // left
22629             case 39: // right
22630                 dir = e.keyCode == 37 ? -1 : 1;
22631                 
22632                 this.vIndex = this.vIndex + dir;
22633                 
22634                 if(this.vIndex < 0){
22635                     this.vIndex = 0;
22636                 }
22637                 
22638                 if(this.vIndex > 11){
22639                     this.vIndex = 11;
22640                 }
22641                 
22642                 if(isNaN(this.vIndex)){
22643                     this.vIndex = 0;
22644                 }
22645                 
22646                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22647                 
22648                 break;
22649             case 38: // up
22650             case 40: // down
22651                 
22652                 dir = e.keyCode == 38 ? -1 : 1;
22653                 
22654                 this.vIndex = this.vIndex + dir * 4;
22655                 
22656                 if(this.vIndex < 0){
22657                     this.vIndex = 0;
22658                 }
22659                 
22660                 if(this.vIndex > 11){
22661                     this.vIndex = 11;
22662                 }
22663                 
22664                 if(isNaN(this.vIndex)){
22665                     this.vIndex = 0;
22666                 }
22667                 
22668                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22669                 break;
22670                 
22671             case 13: // enter
22672                 
22673                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22674                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22675                 }
22676                 
22677                 this.hide();
22678                 e.preventDefault();
22679                 break;
22680             case 9: // tab
22681                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22682                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22683                 }
22684                 this.hide();
22685                 break;
22686             case 16: // shift
22687             case 17: // ctrl
22688             case 18: // alt
22689                 break;
22690             default :
22691                 this.hide();
22692                 
22693         }
22694     },
22695     
22696     remove: function() 
22697     {
22698         this.picker().remove();
22699     }
22700    
22701 });
22702
22703 Roo.apply(Roo.bootstrap.MonthField,  {
22704     
22705     content : {
22706         tag: 'tbody',
22707         cn: [
22708         {
22709             tag: 'tr',
22710             cn: [
22711             {
22712                 tag: 'td',
22713                 colspan: '7'
22714             }
22715             ]
22716         }
22717         ]
22718     },
22719     
22720     dates:{
22721         en: {
22722             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22723             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22724         }
22725     }
22726 });
22727
22728 Roo.apply(Roo.bootstrap.MonthField,  {
22729   
22730     template : {
22731         tag: 'div',
22732         cls: 'datepicker dropdown-menu roo-dynamic',
22733         cn: [
22734             {
22735                 tag: 'div',
22736                 cls: 'datepicker-months',
22737                 cn: [
22738                 {
22739                     tag: 'table',
22740                     cls: 'table-condensed',
22741                     cn:[
22742                         Roo.bootstrap.DateField.content
22743                     ]
22744                 }
22745                 ]
22746             }
22747         ]
22748     }
22749 });
22750
22751  
22752
22753  
22754  /*
22755  * - LGPL
22756  *
22757  * CheckBox
22758  * 
22759  */
22760
22761 /**
22762  * @class Roo.bootstrap.CheckBox
22763  * @extends Roo.bootstrap.Input
22764  * Bootstrap CheckBox class
22765  * 
22766  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22767  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22768  * @cfg {String} boxLabel The text that appears beside the checkbox
22769  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22770  * @cfg {Boolean} checked initnal the element
22771  * @cfg {Boolean} inline inline the element (default false)
22772  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22773  * @cfg {String} tooltip label tooltip
22774  * 
22775  * @constructor
22776  * Create a new CheckBox
22777  * @param {Object} config The config object
22778  */
22779
22780 Roo.bootstrap.CheckBox = function(config){
22781     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22782    
22783     this.addEvents({
22784         /**
22785         * @event check
22786         * Fires when the element is checked or unchecked.
22787         * @param {Roo.bootstrap.CheckBox} this This input
22788         * @param {Boolean} checked The new checked value
22789         */
22790        check : true,
22791        /**
22792         * @event click
22793         * Fires when the element is click.
22794         * @param {Roo.bootstrap.CheckBox} this This input
22795         */
22796        click : true
22797     });
22798     
22799 };
22800
22801 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22802   
22803     inputType: 'checkbox',
22804     inputValue: 1,
22805     valueOff: 0,
22806     boxLabel: false,
22807     checked: false,
22808     weight : false,
22809     inline: false,
22810     tooltip : '',
22811     
22812     // checkbox success does not make any sense really.. 
22813     invalidClass : "",
22814     validClass : "",
22815     
22816     
22817     getAutoCreate : function()
22818     {
22819         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22820         
22821         var id = Roo.id();
22822         
22823         var cfg = {};
22824         
22825         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22826         
22827         if(this.inline){
22828             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22829         }
22830         
22831         var input =  {
22832             tag: 'input',
22833             id : id,
22834             type : this.inputType,
22835             value : this.inputValue,
22836             cls : 'roo-' + this.inputType, //'form-box',
22837             placeholder : this.placeholder || ''
22838             
22839         };
22840         
22841         if(this.inputType != 'radio'){
22842             var hidden =  {
22843                 tag: 'input',
22844                 type : 'hidden',
22845                 cls : 'roo-hidden-value',
22846                 value : this.checked ? this.inputValue : this.valueOff
22847             };
22848         }
22849         
22850             
22851         if (this.weight) { // Validity check?
22852             cfg.cls += " " + this.inputType + "-" + this.weight;
22853         }
22854         
22855         if (this.disabled) {
22856             input.disabled=true;
22857         }
22858         
22859         if(this.checked){
22860             input.checked = this.checked;
22861         }
22862         
22863         if (this.name) {
22864             
22865             input.name = this.name;
22866             
22867             if(this.inputType != 'radio'){
22868                 hidden.name = this.name;
22869                 input.name = '_hidden_' + this.name;
22870             }
22871         }
22872         
22873         if (this.size) {
22874             input.cls += ' input-' + this.size;
22875         }
22876         
22877         var settings=this;
22878         
22879         ['xs','sm','md','lg'].map(function(size){
22880             if (settings[size]) {
22881                 cfg.cls += ' col-' + size + '-' + settings[size];
22882             }
22883         });
22884         
22885         var inputblock = input;
22886          
22887         if (this.before || this.after) {
22888             
22889             inputblock = {
22890                 cls : 'input-group',
22891                 cn :  [] 
22892             };
22893             
22894             if (this.before) {
22895                 inputblock.cn.push({
22896                     tag :'span',
22897                     cls : 'input-group-addon',
22898                     html : this.before
22899                 });
22900             }
22901             
22902             inputblock.cn.push(input);
22903             
22904             if(this.inputType != 'radio'){
22905                 inputblock.cn.push(hidden);
22906             }
22907             
22908             if (this.after) {
22909                 inputblock.cn.push({
22910                     tag :'span',
22911                     cls : 'input-group-addon',
22912                     html : this.after
22913                 });
22914             }
22915             
22916         }
22917         var boxLabelCfg = false;
22918         
22919         if(this.boxLabel){
22920            
22921             boxLabelCfg = {
22922                 tag: 'label',
22923                 //'for': id, // box label is handled by onclick - so no for...
22924                 cls: 'box-label',
22925                 html: this.boxLabel
22926             };
22927             if(this.tooltip){
22928                 boxLabelCfg.tooltip = this.tooltip;
22929             }
22930              
22931         }
22932         
22933         
22934         if (align ==='left' && this.fieldLabel.length) {
22935 //                Roo.log("left and has label");
22936             cfg.cn = [
22937                 {
22938                     tag: 'label',
22939                     'for' :  id,
22940                     cls : 'control-label',
22941                     html : this.fieldLabel
22942                 },
22943                 {
22944                     cls : "", 
22945                     cn: [
22946                         inputblock
22947                     ]
22948                 }
22949             ];
22950             
22951             if (boxLabelCfg) {
22952                 cfg.cn[1].cn.push(boxLabelCfg);
22953             }
22954             
22955             if(this.labelWidth > 12){
22956                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22957             }
22958             
22959             if(this.labelWidth < 13 && this.labelmd == 0){
22960                 this.labelmd = this.labelWidth;
22961             }
22962             
22963             if(this.labellg > 0){
22964                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22965                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22966             }
22967             
22968             if(this.labelmd > 0){
22969                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22970                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22971             }
22972             
22973             if(this.labelsm > 0){
22974                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22975                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22976             }
22977             
22978             if(this.labelxs > 0){
22979                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22980                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22981             }
22982             
22983         } else if ( this.fieldLabel.length) {
22984 //                Roo.log(" label");
22985                 cfg.cn = [
22986                    
22987                     {
22988                         tag: this.boxLabel ? 'span' : 'label',
22989                         'for': id,
22990                         cls: 'control-label box-input-label',
22991                         //cls : 'input-group-addon',
22992                         html : this.fieldLabel
22993                     },
22994                     
22995                     inputblock
22996                     
22997                 ];
22998                 if (boxLabelCfg) {
22999                     cfg.cn.push(boxLabelCfg);
23000                 }
23001
23002         } else {
23003             
23004 //                Roo.log(" no label && no align");
23005                 cfg.cn = [  inputblock ] ;
23006                 if (boxLabelCfg) {
23007                     cfg.cn.push(boxLabelCfg);
23008                 }
23009
23010                 
23011         }
23012         
23013        
23014         
23015         if(this.inputType != 'radio'){
23016             cfg.cn.push(hidden);
23017         }
23018         
23019         return cfg;
23020         
23021     },
23022     
23023     /**
23024      * return the real input element.
23025      */
23026     inputEl: function ()
23027     {
23028         return this.el.select('input.roo-' + this.inputType,true).first();
23029     },
23030     hiddenEl: function ()
23031     {
23032         return this.el.select('input.roo-hidden-value',true).first();
23033     },
23034     
23035     labelEl: function()
23036     {
23037         return this.el.select('label.control-label',true).first();
23038     },
23039     /* depricated... */
23040     
23041     label: function()
23042     {
23043         return this.labelEl();
23044     },
23045     
23046     boxLabelEl: function()
23047     {
23048         return this.el.select('label.box-label',true).first();
23049     },
23050     
23051     initEvents : function()
23052     {
23053 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23054         
23055         this.inputEl().on('click', this.onClick,  this);
23056         
23057         if (this.boxLabel) { 
23058             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23059         }
23060         
23061         this.startValue = this.getValue();
23062         
23063         if(this.groupId){
23064             Roo.bootstrap.CheckBox.register(this);
23065         }
23066     },
23067     
23068     onClick : function(e)
23069     {   
23070         if(this.fireEvent('click', this, e) !== false){
23071             this.setChecked(!this.checked);
23072         }
23073         
23074     },
23075     
23076     setChecked : function(state,suppressEvent)
23077     {
23078         this.startValue = this.getValue();
23079
23080         if(this.inputType == 'radio'){
23081             
23082             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23083                 e.dom.checked = false;
23084             });
23085             
23086             this.inputEl().dom.checked = true;
23087             
23088             this.inputEl().dom.value = this.inputValue;
23089             
23090             if(suppressEvent !== true){
23091                 this.fireEvent('check', this, true);
23092             }
23093             
23094             this.validate();
23095             
23096             return;
23097         }
23098         
23099         this.checked = state;
23100         
23101         this.inputEl().dom.checked = state;
23102         
23103         
23104         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23105         
23106         if(suppressEvent !== true){
23107             this.fireEvent('check', this, state);
23108         }
23109         
23110         this.validate();
23111     },
23112     
23113     getValue : function()
23114     {
23115         if(this.inputType == 'radio'){
23116             return this.getGroupValue();
23117         }
23118         
23119         return this.hiddenEl().dom.value;
23120         
23121     },
23122     
23123     getGroupValue : function()
23124     {
23125         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23126             return '';
23127         }
23128         
23129         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23130     },
23131     
23132     setValue : function(v,suppressEvent)
23133     {
23134         if(this.inputType == 'radio'){
23135             this.setGroupValue(v, suppressEvent);
23136             return;
23137         }
23138         
23139         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23140         
23141         this.validate();
23142     },
23143     
23144     setGroupValue : function(v, suppressEvent)
23145     {
23146         this.startValue = this.getValue();
23147         
23148         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23149             e.dom.checked = false;
23150             
23151             if(e.dom.value == v){
23152                 e.dom.checked = true;
23153             }
23154         });
23155         
23156         if(suppressEvent !== true){
23157             this.fireEvent('check', this, true);
23158         }
23159
23160         this.validate();
23161         
23162         return;
23163     },
23164     
23165     validate : function()
23166     {
23167         if(this.getVisibilityEl().hasClass('hidden')){
23168             return true;
23169         }
23170         
23171         if(
23172                 this.disabled || 
23173                 (this.inputType == 'radio' && this.validateRadio()) ||
23174                 (this.inputType == 'checkbox' && this.validateCheckbox())
23175         ){
23176             this.markValid();
23177             return true;
23178         }
23179         
23180         this.markInvalid();
23181         return false;
23182     },
23183     
23184     validateRadio : function()
23185     {
23186         if(this.getVisibilityEl().hasClass('hidden')){
23187             return true;
23188         }
23189         
23190         if(this.allowBlank){
23191             return true;
23192         }
23193         
23194         var valid = false;
23195         
23196         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23197             if(!e.dom.checked){
23198                 return;
23199             }
23200             
23201             valid = true;
23202             
23203             return false;
23204         });
23205         
23206         return valid;
23207     },
23208     
23209     validateCheckbox : function()
23210     {
23211         if(!this.groupId){
23212             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23213             //return (this.getValue() == this.inputValue) ? true : false;
23214         }
23215         
23216         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23217         
23218         if(!group){
23219             return false;
23220         }
23221         
23222         var r = false;
23223         
23224         for(var i in group){
23225             if(group[i].el.isVisible(true)){
23226                 r = false;
23227                 break;
23228             }
23229             
23230             r = true;
23231         }
23232         
23233         for(var i in group){
23234             if(r){
23235                 break;
23236             }
23237             
23238             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23239         }
23240         
23241         return r;
23242     },
23243     
23244     /**
23245      * Mark this field as valid
23246      */
23247     markValid : function()
23248     {
23249         var _this = this;
23250         
23251         this.fireEvent('valid', this);
23252         
23253         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23254         
23255         if(this.groupId){
23256             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23257         }
23258         
23259         if(label){
23260             label.markValid();
23261         }
23262
23263         if(this.inputType == 'radio'){
23264             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23265                 var fg = e.findParent('.form-group', false, true);
23266                 if (Roo.bootstrap.version == 3) {
23267                     fg.removeClass([_this.invalidClass, _this.validClass]);
23268                     fg.addClass(_this.validClass);
23269                 } else {
23270                     fg.removeClass(['is-valid', 'is-invalid']);
23271                     fg.addClass('is-valid');
23272                 }
23273             });
23274             
23275             return;
23276         }
23277
23278         if(!this.groupId){
23279             var fg = this.el.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             return;
23288         }
23289         
23290         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23291         
23292         if(!group){
23293             return;
23294         }
23295         
23296         for(var i in group){
23297             var fg = group[i].el.findParent('.form-group', false, true);
23298             if (Roo.bootstrap.version == 3) {
23299                 fg.removeClass([this.invalidClass, this.validClass]);
23300                 fg.addClass(this.validClass);
23301             } else {
23302                 fg.removeClass(['is-valid', 'is-invalid']);
23303                 fg.addClass('is-valid');
23304             }
23305         }
23306     },
23307     
23308      /**
23309      * Mark this field as invalid
23310      * @param {String} msg The validation message
23311      */
23312     markInvalid : function(msg)
23313     {
23314         if(this.allowBlank){
23315             return;
23316         }
23317         
23318         var _this = this;
23319         
23320         this.fireEvent('invalid', this, msg);
23321         
23322         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23323         
23324         if(this.groupId){
23325             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23326         }
23327         
23328         if(label){
23329             label.markInvalid();
23330         }
23331             
23332         if(this.inputType == 'radio'){
23333             
23334             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23335                 var fg = e.findParent('.form-group', false, true);
23336                 if (Roo.bootstrap.version == 3) {
23337                     fg.removeClass([_this.invalidClass, _this.validClass]);
23338                     fg.addClass(_this.invalidClass);
23339                 } else {
23340                     fg.removeClass(['is-invalid', 'is-valid']);
23341                     fg.addClass('is-invalid');
23342                 }
23343             });
23344             
23345             return;
23346         }
23347         
23348         if(!this.groupId){
23349             var fg = this.el.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             return;
23358         }
23359         
23360         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23361         
23362         if(!group){
23363             return;
23364         }
23365         
23366         for(var i in group){
23367             var fg = group[i].el.findParent('.form-group', false, true);
23368             if (Roo.bootstrap.version == 3) {
23369                 fg.removeClass([_this.invalidClass, _this.validClass]);
23370                 fg.addClass(_this.invalidClass);
23371             } else {
23372                 fg.removeClass(['is-invalid', 'is-valid']);
23373                 fg.addClass('is-invalid');
23374             }
23375         }
23376         
23377     },
23378     
23379     clearInvalid : function()
23380     {
23381         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23382         
23383         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23384         
23385         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23386         
23387         if (label && label.iconEl) {
23388             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23389             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23390         }
23391     },
23392     
23393     disable : function()
23394     {
23395         if(this.inputType != 'radio'){
23396             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23397             return;
23398         }
23399         
23400         var _this = this;
23401         
23402         if(this.rendered){
23403             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23404                 _this.getActionEl().addClass(this.disabledClass);
23405                 e.dom.disabled = true;
23406             });
23407         }
23408         
23409         this.disabled = true;
23410         this.fireEvent("disable", this);
23411         return this;
23412     },
23413
23414     enable : function()
23415     {
23416         if(this.inputType != 'radio'){
23417             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23418             return;
23419         }
23420         
23421         var _this = this;
23422         
23423         if(this.rendered){
23424             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23425                 _this.getActionEl().removeClass(this.disabledClass);
23426                 e.dom.disabled = false;
23427             });
23428         }
23429         
23430         this.disabled = false;
23431         this.fireEvent("enable", this);
23432         return this;
23433     },
23434     
23435     setBoxLabel : function(v)
23436     {
23437         this.boxLabel = v;
23438         
23439         if(this.rendered){
23440             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23441         }
23442     }
23443
23444 });
23445
23446 Roo.apply(Roo.bootstrap.CheckBox, {
23447     
23448     groups: {},
23449     
23450      /**
23451     * register a CheckBox Group
23452     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23453     */
23454     register : function(checkbox)
23455     {
23456         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23457             this.groups[checkbox.groupId] = {};
23458         }
23459         
23460         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23461             return;
23462         }
23463         
23464         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23465         
23466     },
23467     /**
23468     * fetch a CheckBox Group based on the group ID
23469     * @param {string} the group ID
23470     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23471     */
23472     get: function(groupId) {
23473         if (typeof(this.groups[groupId]) == 'undefined') {
23474             return false;
23475         }
23476         
23477         return this.groups[groupId] ;
23478     }
23479     
23480     
23481 });
23482 /*
23483  * - LGPL
23484  *
23485  * RadioItem
23486  * 
23487  */
23488
23489 /**
23490  * @class Roo.bootstrap.Radio
23491  * @extends Roo.bootstrap.Component
23492  * Bootstrap Radio class
23493  * @cfg {String} boxLabel - the label associated
23494  * @cfg {String} value - the value of radio
23495  * 
23496  * @constructor
23497  * Create a new Radio
23498  * @param {Object} config The config object
23499  */
23500 Roo.bootstrap.Radio = function(config){
23501     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23502     
23503 };
23504
23505 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23506     
23507     boxLabel : '',
23508     
23509     value : '',
23510     
23511     getAutoCreate : function()
23512     {
23513         var cfg = {
23514             tag : 'div',
23515             cls : 'form-group radio',
23516             cn : [
23517                 {
23518                     tag : 'label',
23519                     cls : 'box-label',
23520                     html : this.boxLabel
23521                 }
23522             ]
23523         };
23524         
23525         return cfg;
23526     },
23527     
23528     initEvents : function() 
23529     {
23530         this.parent().register(this);
23531         
23532         this.el.on('click', this.onClick, this);
23533         
23534     },
23535     
23536     onClick : function(e)
23537     {
23538         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23539             this.setChecked(true);
23540         }
23541     },
23542     
23543     setChecked : function(state, suppressEvent)
23544     {
23545         this.parent().setValue(this.value, suppressEvent);
23546         
23547     },
23548     
23549     setBoxLabel : function(v)
23550     {
23551         this.boxLabel = v;
23552         
23553         if(this.rendered){
23554             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23555         }
23556     }
23557     
23558 });
23559  
23560
23561  /*
23562  * - LGPL
23563  *
23564  * Input
23565  * 
23566  */
23567
23568 /**
23569  * @class Roo.bootstrap.SecurePass
23570  * @extends Roo.bootstrap.Input
23571  * Bootstrap SecurePass class
23572  *
23573  * 
23574  * @constructor
23575  * Create a new SecurePass
23576  * @param {Object} config The config object
23577  */
23578  
23579 Roo.bootstrap.SecurePass = function (config) {
23580     // these go here, so the translation tool can replace them..
23581     this.errors = {
23582         PwdEmpty: "Please type a password, and then retype it to confirm.",
23583         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23584         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23585         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23586         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23587         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23588         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23589         TooWeak: "Your password is Too Weak."
23590     },
23591     this.meterLabel = "Password strength:";
23592     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23593     this.meterClass = [
23594         "roo-password-meter-tooweak", 
23595         "roo-password-meter-weak", 
23596         "roo-password-meter-medium", 
23597         "roo-password-meter-strong", 
23598         "roo-password-meter-grey"
23599     ];
23600     
23601     this.errors = {};
23602     
23603     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23604 }
23605
23606 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23607     /**
23608      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23609      * {
23610      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23611      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23612      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23613      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23614      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23615      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23616      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23617      * })
23618      */
23619     // private
23620     
23621     meterWidth: 300,
23622     errorMsg :'',    
23623     errors: false,
23624     imageRoot: '/',
23625     /**
23626      * @cfg {String/Object} Label for the strength meter (defaults to
23627      * 'Password strength:')
23628      */
23629     // private
23630     meterLabel: '',
23631     /**
23632      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23633      * ['Weak', 'Medium', 'Strong'])
23634      */
23635     // private    
23636     pwdStrengths: false,    
23637     // private
23638     strength: 0,
23639     // private
23640     _lastPwd: null,
23641     // private
23642     kCapitalLetter: 0,
23643     kSmallLetter: 1,
23644     kDigit: 2,
23645     kPunctuation: 3,
23646     
23647     insecure: false,
23648     // private
23649     initEvents: function ()
23650     {
23651         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23652
23653         if (this.el.is('input[type=password]') && Roo.isSafari) {
23654             this.el.on('keydown', this.SafariOnKeyDown, this);
23655         }
23656
23657         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23658     },
23659     // private
23660     onRender: function (ct, position)
23661     {
23662         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23663         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23664         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23665
23666         this.trigger.createChild({
23667                    cn: [
23668                     {
23669                     //id: 'PwdMeter',
23670                     tag: 'div',
23671                     cls: 'roo-password-meter-grey col-xs-12',
23672                     style: {
23673                         //width: 0,
23674                         //width: this.meterWidth + 'px'                                                
23675                         }
23676                     },
23677                     {                            
23678                          cls: 'roo-password-meter-text'                          
23679                     }
23680                 ]            
23681         });
23682
23683          
23684         if (this.hideTrigger) {
23685             this.trigger.setDisplayed(false);
23686         }
23687         this.setSize(this.width || '', this.height || '');
23688     },
23689     // private
23690     onDestroy: function ()
23691     {
23692         if (this.trigger) {
23693             this.trigger.removeAllListeners();
23694             this.trigger.remove();
23695         }
23696         if (this.wrap) {
23697             this.wrap.remove();
23698         }
23699         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23700     },
23701     // private
23702     checkStrength: function ()
23703     {
23704         var pwd = this.inputEl().getValue();
23705         if (pwd == this._lastPwd) {
23706             return;
23707         }
23708
23709         var strength;
23710         if (this.ClientSideStrongPassword(pwd)) {
23711             strength = 3;
23712         } else if (this.ClientSideMediumPassword(pwd)) {
23713             strength = 2;
23714         } else if (this.ClientSideWeakPassword(pwd)) {
23715             strength = 1;
23716         } else {
23717             strength = 0;
23718         }
23719         
23720         Roo.log('strength1: ' + strength);
23721         
23722         //var pm = this.trigger.child('div/div/div').dom;
23723         var pm = this.trigger.child('div/div');
23724         pm.removeClass(this.meterClass);
23725         pm.addClass(this.meterClass[strength]);
23726                 
23727         
23728         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23729                 
23730         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23731         
23732         this._lastPwd = pwd;
23733     },
23734     reset: function ()
23735     {
23736         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23737         
23738         this._lastPwd = '';
23739         
23740         var pm = this.trigger.child('div/div');
23741         pm.removeClass(this.meterClass);
23742         pm.addClass('roo-password-meter-grey');        
23743         
23744         
23745         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23746         
23747         pt.innerHTML = '';
23748         this.inputEl().dom.type='password';
23749     },
23750     // private
23751     validateValue: function (value)
23752     {
23753         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23754             return false;
23755         }
23756         if (value.length == 0) {
23757             if (this.allowBlank) {
23758                 this.clearInvalid();
23759                 return true;
23760             }
23761
23762             this.markInvalid(this.errors.PwdEmpty);
23763             this.errorMsg = this.errors.PwdEmpty;
23764             return false;
23765         }
23766         
23767         if(this.insecure){
23768             return true;
23769         }
23770         
23771         if (!value.match(/[\x21-\x7e]+/)) {
23772             this.markInvalid(this.errors.PwdBadChar);
23773             this.errorMsg = this.errors.PwdBadChar;
23774             return false;
23775         }
23776         if (value.length < 6) {
23777             this.markInvalid(this.errors.PwdShort);
23778             this.errorMsg = this.errors.PwdShort;
23779             return false;
23780         }
23781         if (value.length > 16) {
23782             this.markInvalid(this.errors.PwdLong);
23783             this.errorMsg = this.errors.PwdLong;
23784             return false;
23785         }
23786         var strength;
23787         if (this.ClientSideStrongPassword(value)) {
23788             strength = 3;
23789         } else if (this.ClientSideMediumPassword(value)) {
23790             strength = 2;
23791         } else if (this.ClientSideWeakPassword(value)) {
23792             strength = 1;
23793         } else {
23794             strength = 0;
23795         }
23796
23797         
23798         if (strength < 2) {
23799             //this.markInvalid(this.errors.TooWeak);
23800             this.errorMsg = this.errors.TooWeak;
23801             //return false;
23802         }
23803         
23804         
23805         console.log('strength2: ' + strength);
23806         
23807         //var pm = this.trigger.child('div/div/div').dom;
23808         
23809         var pm = this.trigger.child('div/div');
23810         pm.removeClass(this.meterClass);
23811         pm.addClass(this.meterClass[strength]);
23812                 
23813         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23814                 
23815         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23816         
23817         this.errorMsg = ''; 
23818         return true;
23819     },
23820     // private
23821     CharacterSetChecks: function (type)
23822     {
23823         this.type = type;
23824         this.fResult = false;
23825     },
23826     // private
23827     isctype: function (character, type)
23828     {
23829         switch (type) {  
23830             case this.kCapitalLetter:
23831                 if (character >= 'A' && character <= 'Z') {
23832                     return true;
23833                 }
23834                 break;
23835             
23836             case this.kSmallLetter:
23837                 if (character >= 'a' && character <= 'z') {
23838                     return true;
23839                 }
23840                 break;
23841             
23842             case this.kDigit:
23843                 if (character >= '0' && character <= '9') {
23844                     return true;
23845                 }
23846                 break;
23847             
23848             case this.kPunctuation:
23849                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23850                     return true;
23851                 }
23852                 break;
23853             
23854             default:
23855                 return false;
23856         }
23857
23858     },
23859     // private
23860     IsLongEnough: function (pwd, size)
23861     {
23862         return !(pwd == null || isNaN(size) || pwd.length < size);
23863     },
23864     // private
23865     SpansEnoughCharacterSets: function (word, nb)
23866     {
23867         if (!this.IsLongEnough(word, nb))
23868         {
23869             return false;
23870         }
23871
23872         var characterSetChecks = new Array(
23873             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23874             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23875         );
23876         
23877         for (var index = 0; index < word.length; ++index) {
23878             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23879                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23880                     characterSetChecks[nCharSet].fResult = true;
23881                     break;
23882                 }
23883             }
23884         }
23885
23886         var nCharSets = 0;
23887         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23888             if (characterSetChecks[nCharSet].fResult) {
23889                 ++nCharSets;
23890             }
23891         }
23892
23893         if (nCharSets < nb) {
23894             return false;
23895         }
23896         return true;
23897     },
23898     // private
23899     ClientSideStrongPassword: function (pwd)
23900     {
23901         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23902     },
23903     // private
23904     ClientSideMediumPassword: function (pwd)
23905     {
23906         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23907     },
23908     // private
23909     ClientSideWeakPassword: function (pwd)
23910     {
23911         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23912     }
23913           
23914 })//<script type="text/javascript">
23915
23916 /*
23917  * Based  Ext JS Library 1.1.1
23918  * Copyright(c) 2006-2007, Ext JS, LLC.
23919  * LGPL
23920  *
23921  */
23922  
23923 /**
23924  * @class Roo.HtmlEditorCore
23925  * @extends Roo.Component
23926  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23927  *
23928  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23929  */
23930
23931 Roo.HtmlEditorCore = function(config){
23932     
23933     
23934     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23935     
23936     
23937     this.addEvents({
23938         /**
23939          * @event initialize
23940          * Fires when the editor is fully initialized (including the iframe)
23941          * @param {Roo.HtmlEditorCore} this
23942          */
23943         initialize: true,
23944         /**
23945          * @event activate
23946          * Fires when the editor is first receives the focus. Any insertion must wait
23947          * until after this event.
23948          * @param {Roo.HtmlEditorCore} this
23949          */
23950         activate: true,
23951          /**
23952          * @event beforesync
23953          * Fires before the textarea is updated with content from the editor iframe. Return false
23954          * to cancel the sync.
23955          * @param {Roo.HtmlEditorCore} this
23956          * @param {String} html
23957          */
23958         beforesync: true,
23959          /**
23960          * @event beforepush
23961          * Fires before the iframe editor is updated with content from the textarea. Return false
23962          * to cancel the push.
23963          * @param {Roo.HtmlEditorCore} this
23964          * @param {String} html
23965          */
23966         beforepush: true,
23967          /**
23968          * @event sync
23969          * Fires when the textarea is updated with content from the editor iframe.
23970          * @param {Roo.HtmlEditorCore} this
23971          * @param {String} html
23972          */
23973         sync: true,
23974          /**
23975          * @event push
23976          * Fires when the iframe editor is updated with content from the textarea.
23977          * @param {Roo.HtmlEditorCore} this
23978          * @param {String} html
23979          */
23980         push: true,
23981         
23982         /**
23983          * @event editorevent
23984          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23985          * @param {Roo.HtmlEditorCore} this
23986          */
23987         editorevent: true
23988         
23989     });
23990     
23991     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23992     
23993     // defaults : white / black...
23994     this.applyBlacklists();
23995     
23996     
23997     
23998 };
23999
24000
24001 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24002
24003
24004      /**
24005      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24006      */
24007     
24008     owner : false,
24009     
24010      /**
24011      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24012      *                        Roo.resizable.
24013      */
24014     resizable : false,
24015      /**
24016      * @cfg {Number} height (in pixels)
24017      */   
24018     height: 300,
24019    /**
24020      * @cfg {Number} width (in pixels)
24021      */   
24022     width: 500,
24023     
24024     /**
24025      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24026      * 
24027      */
24028     stylesheets: false,
24029     
24030     // id of frame..
24031     frameId: false,
24032     
24033     // private properties
24034     validationEvent : false,
24035     deferHeight: true,
24036     initialized : false,
24037     activated : false,
24038     sourceEditMode : false,
24039     onFocus : Roo.emptyFn,
24040     iframePad:3,
24041     hideMode:'offsets',
24042     
24043     clearUp: true,
24044     
24045     // blacklist + whitelisted elements..
24046     black: false,
24047     white: false,
24048      
24049     bodyCls : '',
24050
24051     /**
24052      * Protected method that will not generally be called directly. It
24053      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24054      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24055      */
24056     getDocMarkup : function(){
24057         // body styles..
24058         var st = '';
24059         
24060         // inherit styels from page...?? 
24061         if (this.stylesheets === false) {
24062             
24063             Roo.get(document.head).select('style').each(function(node) {
24064                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24065             });
24066             
24067             Roo.get(document.head).select('link').each(function(node) { 
24068                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24069             });
24070             
24071         } else if (!this.stylesheets.length) {
24072                 // simple..
24073                 st = '<style type="text/css">' +
24074                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24075                    '</style>';
24076         } else {
24077             for (var i in this.stylesheets) { 
24078                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24079             }
24080             
24081         }
24082         
24083         st +=  '<style type="text/css">' +
24084             'IMG { cursor: pointer } ' +
24085         '</style>';
24086
24087         var cls = 'roo-htmleditor-body';
24088         
24089         if(this.bodyCls.length){
24090             cls += ' ' + this.bodyCls;
24091         }
24092         
24093         return '<html><head>' + st  +
24094             //<style type="text/css">' +
24095             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24096             //'</style>' +
24097             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24098     },
24099
24100     // private
24101     onRender : function(ct, position)
24102     {
24103         var _t = this;
24104         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24105         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24106         
24107         
24108         this.el.dom.style.border = '0 none';
24109         this.el.dom.setAttribute('tabIndex', -1);
24110         this.el.addClass('x-hidden hide');
24111         
24112         
24113         
24114         if(Roo.isIE){ // fix IE 1px bogus margin
24115             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24116         }
24117        
24118         
24119         this.frameId = Roo.id();
24120         
24121          
24122         
24123         var iframe = this.owner.wrap.createChild({
24124             tag: 'iframe',
24125             cls: 'form-control', // bootstrap..
24126             id: this.frameId,
24127             name: this.frameId,
24128             frameBorder : 'no',
24129             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24130         }, this.el
24131         );
24132         
24133         
24134         this.iframe = iframe.dom;
24135
24136          this.assignDocWin();
24137         
24138         this.doc.designMode = 'on';
24139        
24140         this.doc.open();
24141         this.doc.write(this.getDocMarkup());
24142         this.doc.close();
24143
24144         
24145         var task = { // must defer to wait for browser to be ready
24146             run : function(){
24147                 //console.log("run task?" + this.doc.readyState);
24148                 this.assignDocWin();
24149                 if(this.doc.body || this.doc.readyState == 'complete'){
24150                     try {
24151                         this.doc.designMode="on";
24152                     } catch (e) {
24153                         return;
24154                     }
24155                     Roo.TaskMgr.stop(task);
24156                     this.initEditor.defer(10, this);
24157                 }
24158             },
24159             interval : 10,
24160             duration: 10000,
24161             scope: this
24162         };
24163         Roo.TaskMgr.start(task);
24164
24165     },
24166
24167     // private
24168     onResize : function(w, h)
24169     {
24170          Roo.log('resize: ' +w + ',' + h );
24171         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24172         if(!this.iframe){
24173             return;
24174         }
24175         if(typeof w == 'number'){
24176             
24177             this.iframe.style.width = w + 'px';
24178         }
24179         if(typeof h == 'number'){
24180             
24181             this.iframe.style.height = h + 'px';
24182             if(this.doc){
24183                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24184             }
24185         }
24186         
24187     },
24188
24189     /**
24190      * Toggles the editor between standard and source edit mode.
24191      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24192      */
24193     toggleSourceEdit : function(sourceEditMode){
24194         
24195         this.sourceEditMode = sourceEditMode === true;
24196         
24197         if(this.sourceEditMode){
24198  
24199             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24200             
24201         }else{
24202             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24203             //this.iframe.className = '';
24204             this.deferFocus();
24205         }
24206         //this.setSize(this.owner.wrap.getSize());
24207         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24208     },
24209
24210     
24211   
24212
24213     /**
24214      * Protected method that will not generally be called directly. If you need/want
24215      * custom HTML cleanup, this is the method you should override.
24216      * @param {String} html The HTML to be cleaned
24217      * return {String} The cleaned HTML
24218      */
24219     cleanHtml : function(html){
24220         html = String(html);
24221         if(html.length > 5){
24222             if(Roo.isSafari){ // strip safari nonsense
24223                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24224             }
24225         }
24226         if(html == '&nbsp;'){
24227             html = '';
24228         }
24229         return html;
24230     },
24231
24232     /**
24233      * HTML Editor -> Textarea
24234      * Protected method that will not generally be called directly. Syncs the contents
24235      * of the editor iframe with the textarea.
24236      */
24237     syncValue : function(){
24238         if(this.initialized){
24239             var bd = (this.doc.body || this.doc.documentElement);
24240             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24241             var html = bd.innerHTML;
24242             if(Roo.isSafari){
24243                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24244                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24245                 if(m && m[1]){
24246                     html = '<div style="'+m[0]+'">' + html + '</div>';
24247                 }
24248             }
24249             html = this.cleanHtml(html);
24250             // fix up the special chars.. normaly like back quotes in word...
24251             // however we do not want to do this with chinese..
24252             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24253                 
24254                 var cc = match.charCodeAt();
24255
24256                 // Get the character value, handling surrogate pairs
24257                 if (match.length == 2) {
24258                     // It's a surrogate pair, calculate the Unicode code point
24259                     var high = match.charCodeAt(0) - 0xD800;
24260                     var low  = match.charCodeAt(1) - 0xDC00;
24261                     cc = (high * 0x400) + low + 0x10000;
24262                 }  else if (
24263                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24264                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24265                     (cc >= 0xf900 && cc < 0xfb00 )
24266                 ) {
24267                         return match;
24268                 }  
24269          
24270                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24271                 return "&#" + cc + ";";
24272                 
24273                 
24274             });
24275             
24276             
24277              
24278             if(this.owner.fireEvent('beforesync', this, html) !== false){
24279                 this.el.dom.value = html;
24280                 this.owner.fireEvent('sync', this, html);
24281             }
24282         }
24283     },
24284
24285     /**
24286      * Protected method that will not generally be called directly. Pushes the value of the textarea
24287      * into the iframe editor.
24288      */
24289     pushValue : function(){
24290         if(this.initialized){
24291             var v = this.el.dom.value.trim();
24292             
24293 //            if(v.length < 1){
24294 //                v = '&#160;';
24295 //            }
24296             
24297             if(this.owner.fireEvent('beforepush', this, v) !== false){
24298                 var d = (this.doc.body || this.doc.documentElement);
24299                 d.innerHTML = v;
24300                 this.cleanUpPaste();
24301                 this.el.dom.value = d.innerHTML;
24302                 this.owner.fireEvent('push', this, v);
24303             }
24304         }
24305     },
24306
24307     // private
24308     deferFocus : function(){
24309         this.focus.defer(10, this);
24310     },
24311
24312     // doc'ed in Field
24313     focus : function(){
24314         if(this.win && !this.sourceEditMode){
24315             this.win.focus();
24316         }else{
24317             this.el.focus();
24318         }
24319     },
24320     
24321     assignDocWin: function()
24322     {
24323         var iframe = this.iframe;
24324         
24325          if(Roo.isIE){
24326             this.doc = iframe.contentWindow.document;
24327             this.win = iframe.contentWindow;
24328         } else {
24329 //            if (!Roo.get(this.frameId)) {
24330 //                return;
24331 //            }
24332 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24333 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24334             
24335             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24336                 return;
24337             }
24338             
24339             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24340             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24341         }
24342     },
24343     
24344     // private
24345     initEditor : function(){
24346         //console.log("INIT EDITOR");
24347         this.assignDocWin();
24348         
24349         
24350         
24351         this.doc.designMode="on";
24352         this.doc.open();
24353         this.doc.write(this.getDocMarkup());
24354         this.doc.close();
24355         
24356         var dbody = (this.doc.body || this.doc.documentElement);
24357         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24358         // this copies styles from the containing element into thsi one..
24359         // not sure why we need all of this..
24360         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24361         
24362         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24363         //ss['background-attachment'] = 'fixed'; // w3c
24364         dbody.bgProperties = 'fixed'; // ie
24365         //Roo.DomHelper.applyStyles(dbody, ss);
24366         Roo.EventManager.on(this.doc, {
24367             //'mousedown': this.onEditorEvent,
24368             'mouseup': this.onEditorEvent,
24369             'dblclick': this.onEditorEvent,
24370             'click': this.onEditorEvent,
24371             'keyup': this.onEditorEvent,
24372             buffer:100,
24373             scope: this
24374         });
24375         if(Roo.isGecko){
24376             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24377         }
24378         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24379             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24380         }
24381         this.initialized = true;
24382
24383         this.owner.fireEvent('initialize', this);
24384         this.pushValue();
24385     },
24386
24387     // private
24388     onDestroy : function(){
24389         
24390         
24391         
24392         if(this.rendered){
24393             
24394             //for (var i =0; i < this.toolbars.length;i++) {
24395             //    // fixme - ask toolbars for heights?
24396             //    this.toolbars[i].onDestroy();
24397            // }
24398             
24399             //this.wrap.dom.innerHTML = '';
24400             //this.wrap.remove();
24401         }
24402     },
24403
24404     // private
24405     onFirstFocus : function(){
24406         
24407         this.assignDocWin();
24408         
24409         
24410         this.activated = true;
24411          
24412     
24413         if(Roo.isGecko){ // prevent silly gecko errors
24414             this.win.focus();
24415             var s = this.win.getSelection();
24416             if(!s.focusNode || s.focusNode.nodeType != 3){
24417                 var r = s.getRangeAt(0);
24418                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24419                 r.collapse(true);
24420                 this.deferFocus();
24421             }
24422             try{
24423                 this.execCmd('useCSS', true);
24424                 this.execCmd('styleWithCSS', false);
24425             }catch(e){}
24426         }
24427         this.owner.fireEvent('activate', this);
24428     },
24429
24430     // private
24431     adjustFont: function(btn){
24432         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24433         //if(Roo.isSafari){ // safari
24434         //    adjust *= 2;
24435        // }
24436         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24437         if(Roo.isSafari){ // safari
24438             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24439             v =  (v < 10) ? 10 : v;
24440             v =  (v > 48) ? 48 : v;
24441             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24442             
24443         }
24444         
24445         
24446         v = Math.max(1, v+adjust);
24447         
24448         this.execCmd('FontSize', v  );
24449     },
24450
24451     onEditorEvent : function(e)
24452     {
24453         this.owner.fireEvent('editorevent', this, e);
24454       //  this.updateToolbar();
24455         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24456     },
24457
24458     insertTag : function(tg)
24459     {
24460         // could be a bit smarter... -> wrap the current selected tRoo..
24461         if (tg.toLowerCase() == 'span' ||
24462             tg.toLowerCase() == 'code' ||
24463             tg.toLowerCase() == 'sup' ||
24464             tg.toLowerCase() == 'sub' 
24465             ) {
24466             
24467             range = this.createRange(this.getSelection());
24468             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24469             wrappingNode.appendChild(range.extractContents());
24470             range.insertNode(wrappingNode);
24471
24472             return;
24473             
24474             
24475             
24476         }
24477         this.execCmd("formatblock",   tg);
24478         
24479     },
24480     
24481     insertText : function(txt)
24482     {
24483         
24484         
24485         var range = this.createRange();
24486         range.deleteContents();
24487                //alert(Sender.getAttribute('label'));
24488                
24489         range.insertNode(this.doc.createTextNode(txt));
24490     } ,
24491     
24492      
24493
24494     /**
24495      * Executes a Midas editor command on the editor document and performs necessary focus and
24496      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24497      * @param {String} cmd The Midas command
24498      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24499      */
24500     relayCmd : function(cmd, value){
24501         this.win.focus();
24502         this.execCmd(cmd, value);
24503         this.owner.fireEvent('editorevent', this);
24504         //this.updateToolbar();
24505         this.owner.deferFocus();
24506     },
24507
24508     /**
24509      * Executes a Midas editor command directly on the editor document.
24510      * For visual commands, you should use {@link #relayCmd} instead.
24511      * <b>This should only be called after the editor is initialized.</b>
24512      * @param {String} cmd The Midas command
24513      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24514      */
24515     execCmd : function(cmd, value){
24516         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24517         this.syncValue();
24518     },
24519  
24520  
24521    
24522     /**
24523      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24524      * to insert tRoo.
24525      * @param {String} text | dom node.. 
24526      */
24527     insertAtCursor : function(text)
24528     {
24529         
24530         if(!this.activated){
24531             return;
24532         }
24533         /*
24534         if(Roo.isIE){
24535             this.win.focus();
24536             var r = this.doc.selection.createRange();
24537             if(r){
24538                 r.collapse(true);
24539                 r.pasteHTML(text);
24540                 this.syncValue();
24541                 this.deferFocus();
24542             
24543             }
24544             return;
24545         }
24546         */
24547         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24548             this.win.focus();
24549             
24550             
24551             // from jquery ui (MIT licenced)
24552             var range, node;
24553             var win = this.win;
24554             
24555             if (win.getSelection && win.getSelection().getRangeAt) {
24556                 range = win.getSelection().getRangeAt(0);
24557                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24558                 range.insertNode(node);
24559             } else if (win.document.selection && win.document.selection.createRange) {
24560                 // no firefox support
24561                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24562                 win.document.selection.createRange().pasteHTML(txt);
24563             } else {
24564                 // no firefox support
24565                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24566                 this.execCmd('InsertHTML', txt);
24567             } 
24568             
24569             this.syncValue();
24570             
24571             this.deferFocus();
24572         }
24573     },
24574  // private
24575     mozKeyPress : function(e){
24576         if(e.ctrlKey){
24577             var c = e.getCharCode(), cmd;
24578           
24579             if(c > 0){
24580                 c = String.fromCharCode(c).toLowerCase();
24581                 switch(c){
24582                     case 'b':
24583                         cmd = 'bold';
24584                         break;
24585                     case 'i':
24586                         cmd = 'italic';
24587                         break;
24588                     
24589                     case 'u':
24590                         cmd = 'underline';
24591                         break;
24592                     
24593                     case 'v':
24594                         this.cleanUpPaste.defer(100, this);
24595                         return;
24596                         
24597                 }
24598                 if(cmd){
24599                     this.win.focus();
24600                     this.execCmd(cmd);
24601                     this.deferFocus();
24602                     e.preventDefault();
24603                 }
24604                 
24605             }
24606         }
24607     },
24608
24609     // private
24610     fixKeys : function(){ // load time branching for fastest keydown performance
24611         if(Roo.isIE){
24612             return function(e){
24613                 var k = e.getKey(), r;
24614                 if(k == e.TAB){
24615                     e.stopEvent();
24616                     r = this.doc.selection.createRange();
24617                     if(r){
24618                         r.collapse(true);
24619                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24620                         this.deferFocus();
24621                     }
24622                     return;
24623                 }
24624                 
24625                 if(k == e.ENTER){
24626                     r = this.doc.selection.createRange();
24627                     if(r){
24628                         var target = r.parentElement();
24629                         if(!target || target.tagName.toLowerCase() != 'li'){
24630                             e.stopEvent();
24631                             r.pasteHTML('<br />');
24632                             r.collapse(false);
24633                             r.select();
24634                         }
24635                     }
24636                 }
24637                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24638                     this.cleanUpPaste.defer(100, this);
24639                     return;
24640                 }
24641                 
24642                 
24643             };
24644         }else if(Roo.isOpera){
24645             return function(e){
24646                 var k = e.getKey();
24647                 if(k == e.TAB){
24648                     e.stopEvent();
24649                     this.win.focus();
24650                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24651                     this.deferFocus();
24652                 }
24653                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24654                     this.cleanUpPaste.defer(100, this);
24655                     return;
24656                 }
24657                 
24658             };
24659         }else if(Roo.isSafari){
24660             return function(e){
24661                 var k = e.getKey();
24662                 
24663                 if(k == e.TAB){
24664                     e.stopEvent();
24665                     this.execCmd('InsertText','\t');
24666                     this.deferFocus();
24667                     return;
24668                 }
24669                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24670                     this.cleanUpPaste.defer(100, this);
24671                     return;
24672                 }
24673                 
24674              };
24675         }
24676     }(),
24677     
24678     getAllAncestors: function()
24679     {
24680         var p = this.getSelectedNode();
24681         var a = [];
24682         if (!p) {
24683             a.push(p); // push blank onto stack..
24684             p = this.getParentElement();
24685         }
24686         
24687         
24688         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24689             a.push(p);
24690             p = p.parentNode;
24691         }
24692         a.push(this.doc.body);
24693         return a;
24694     },
24695     lastSel : false,
24696     lastSelNode : false,
24697     
24698     
24699     getSelection : function() 
24700     {
24701         this.assignDocWin();
24702         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24703     },
24704     
24705     getSelectedNode: function() 
24706     {
24707         // this may only work on Gecko!!!
24708         
24709         // should we cache this!!!!
24710         
24711         
24712         
24713          
24714         var range = this.createRange(this.getSelection()).cloneRange();
24715         
24716         if (Roo.isIE) {
24717             var parent = range.parentElement();
24718             while (true) {
24719                 var testRange = range.duplicate();
24720                 testRange.moveToElementText(parent);
24721                 if (testRange.inRange(range)) {
24722                     break;
24723                 }
24724                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24725                     break;
24726                 }
24727                 parent = parent.parentElement;
24728             }
24729             return parent;
24730         }
24731         
24732         // is ancestor a text element.
24733         var ac =  range.commonAncestorContainer;
24734         if (ac.nodeType == 3) {
24735             ac = ac.parentNode;
24736         }
24737         
24738         var ar = ac.childNodes;
24739          
24740         var nodes = [];
24741         var other_nodes = [];
24742         var has_other_nodes = false;
24743         for (var i=0;i<ar.length;i++) {
24744             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24745                 continue;
24746             }
24747             // fullly contained node.
24748             
24749             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24750                 nodes.push(ar[i]);
24751                 continue;
24752             }
24753             
24754             // probably selected..
24755             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24756                 other_nodes.push(ar[i]);
24757                 continue;
24758             }
24759             // outer..
24760             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24761                 continue;
24762             }
24763             
24764             
24765             has_other_nodes = true;
24766         }
24767         if (!nodes.length && other_nodes.length) {
24768             nodes= other_nodes;
24769         }
24770         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24771             return false;
24772         }
24773         
24774         return nodes[0];
24775     },
24776     createRange: function(sel)
24777     {
24778         // this has strange effects when using with 
24779         // top toolbar - not sure if it's a great idea.
24780         //this.editor.contentWindow.focus();
24781         if (typeof sel != "undefined") {
24782             try {
24783                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24784             } catch(e) {
24785                 return this.doc.createRange();
24786             }
24787         } else {
24788             return this.doc.createRange();
24789         }
24790     },
24791     getParentElement: function()
24792     {
24793         
24794         this.assignDocWin();
24795         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24796         
24797         var range = this.createRange(sel);
24798          
24799         try {
24800             var p = range.commonAncestorContainer;
24801             while (p.nodeType == 3) { // text node
24802                 p = p.parentNode;
24803             }
24804             return p;
24805         } catch (e) {
24806             return null;
24807         }
24808     
24809     },
24810     /***
24811      *
24812      * Range intersection.. the hard stuff...
24813      *  '-1' = before
24814      *  '0' = hits..
24815      *  '1' = after.
24816      *         [ -- selected range --- ]
24817      *   [fail]                        [fail]
24818      *
24819      *    basically..
24820      *      if end is before start or  hits it. fail.
24821      *      if start is after end or hits it fail.
24822      *
24823      *   if either hits (but other is outside. - then it's not 
24824      *   
24825      *    
24826      **/
24827     
24828     
24829     // @see http://www.thismuchiknow.co.uk/?p=64.
24830     rangeIntersectsNode : function(range, node)
24831     {
24832         var nodeRange = node.ownerDocument.createRange();
24833         try {
24834             nodeRange.selectNode(node);
24835         } catch (e) {
24836             nodeRange.selectNodeContents(node);
24837         }
24838     
24839         var rangeStartRange = range.cloneRange();
24840         rangeStartRange.collapse(true);
24841     
24842         var rangeEndRange = range.cloneRange();
24843         rangeEndRange.collapse(false);
24844     
24845         var nodeStartRange = nodeRange.cloneRange();
24846         nodeStartRange.collapse(true);
24847     
24848         var nodeEndRange = nodeRange.cloneRange();
24849         nodeEndRange.collapse(false);
24850     
24851         return rangeStartRange.compareBoundaryPoints(
24852                  Range.START_TO_START, nodeEndRange) == -1 &&
24853                rangeEndRange.compareBoundaryPoints(
24854                  Range.START_TO_START, nodeStartRange) == 1;
24855         
24856          
24857     },
24858     rangeCompareNode : function(range, node)
24859     {
24860         var nodeRange = node.ownerDocument.createRange();
24861         try {
24862             nodeRange.selectNode(node);
24863         } catch (e) {
24864             nodeRange.selectNodeContents(node);
24865         }
24866         
24867         
24868         range.collapse(true);
24869     
24870         nodeRange.collapse(true);
24871      
24872         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24873         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24874          
24875         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24876         
24877         var nodeIsBefore   =  ss == 1;
24878         var nodeIsAfter    = ee == -1;
24879         
24880         if (nodeIsBefore && nodeIsAfter) {
24881             return 0; // outer
24882         }
24883         if (!nodeIsBefore && nodeIsAfter) {
24884             return 1; //right trailed.
24885         }
24886         
24887         if (nodeIsBefore && !nodeIsAfter) {
24888             return 2;  // left trailed.
24889         }
24890         // fully contined.
24891         return 3;
24892     },
24893
24894     // private? - in a new class?
24895     cleanUpPaste :  function()
24896     {
24897         // cleans up the whole document..
24898         Roo.log('cleanuppaste');
24899         
24900         this.cleanUpChildren(this.doc.body);
24901         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24902         if (clean != this.doc.body.innerHTML) {
24903             this.doc.body.innerHTML = clean;
24904         }
24905         
24906     },
24907     
24908     cleanWordChars : function(input) {// change the chars to hex code
24909         var he = Roo.HtmlEditorCore;
24910         
24911         var output = input;
24912         Roo.each(he.swapCodes, function(sw) { 
24913             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24914             
24915             output = output.replace(swapper, sw[1]);
24916         });
24917         
24918         return output;
24919     },
24920     
24921     
24922     cleanUpChildren : function (n)
24923     {
24924         if (!n.childNodes.length) {
24925             return;
24926         }
24927         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24928            this.cleanUpChild(n.childNodes[i]);
24929         }
24930     },
24931     
24932     
24933         
24934     
24935     cleanUpChild : function (node)
24936     {
24937         var ed = this;
24938         //console.log(node);
24939         if (node.nodeName == "#text") {
24940             // clean up silly Windows -- stuff?
24941             return; 
24942         }
24943         if (node.nodeName == "#comment") {
24944             node.parentNode.removeChild(node);
24945             // clean up silly Windows -- stuff?
24946             return; 
24947         }
24948         var lcname = node.tagName.toLowerCase();
24949         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24950         // whitelist of tags..
24951         
24952         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24953             // remove node.
24954             node.parentNode.removeChild(node);
24955             return;
24956             
24957         }
24958         
24959         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24960         
24961         // spans with no attributes - just remove them..
24962         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24963             remove_keep_children = true;
24964         }
24965         
24966         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24967         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24968         
24969         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24970         //    remove_keep_children = true;
24971         //}
24972         
24973         if (remove_keep_children) {
24974             this.cleanUpChildren(node);
24975             // inserts everything just before this node...
24976             while (node.childNodes.length) {
24977                 var cn = node.childNodes[0];
24978                 node.removeChild(cn);
24979                 node.parentNode.insertBefore(cn, node);
24980             }
24981             node.parentNode.removeChild(node);
24982             return;
24983         }
24984         
24985         if (!node.attributes || !node.attributes.length) {
24986             
24987           
24988             
24989             
24990             this.cleanUpChildren(node);
24991             return;
24992         }
24993         
24994         function cleanAttr(n,v)
24995         {
24996             
24997             if (v.match(/^\./) || v.match(/^\//)) {
24998                 return;
24999             }
25000             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25001                 return;
25002             }
25003             if (v.match(/^#/)) {
25004                 return;
25005             }
25006             if (v.match(/^\{/)) { // allow template editing.
25007                 return;
25008             }
25009 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25010             node.removeAttribute(n);
25011             
25012         }
25013         
25014         var cwhite = this.cwhite;
25015         var cblack = this.cblack;
25016             
25017         function cleanStyle(n,v)
25018         {
25019             if (v.match(/expression/)) { //XSS?? should we even bother..
25020                 node.removeAttribute(n);
25021                 return;
25022             }
25023             
25024             var parts = v.split(/;/);
25025             var clean = [];
25026             
25027             Roo.each(parts, function(p) {
25028                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25029                 if (!p.length) {
25030                     return true;
25031                 }
25032                 var l = p.split(':').shift().replace(/\s+/g,'');
25033                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25034                 
25035                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25036 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25037                     //node.removeAttribute(n);
25038                     return true;
25039                 }
25040                 //Roo.log()
25041                 // only allow 'c whitelisted system attributes'
25042                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25043 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25044                     //node.removeAttribute(n);
25045                     return true;
25046                 }
25047                 
25048                 
25049                  
25050                 
25051                 clean.push(p);
25052                 return true;
25053             });
25054             if (clean.length) { 
25055                 node.setAttribute(n, clean.join(';'));
25056             } else {
25057                 node.removeAttribute(n);
25058             }
25059             
25060         }
25061         
25062         
25063         for (var i = node.attributes.length-1; i > -1 ; i--) {
25064             var a = node.attributes[i];
25065             //console.log(a);
25066             
25067             if (a.name.toLowerCase().substr(0,2)=='on')  {
25068                 node.removeAttribute(a.name);
25069                 continue;
25070             }
25071             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25072                 node.removeAttribute(a.name);
25073                 continue;
25074             }
25075             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25076                 cleanAttr(a.name,a.value); // fixme..
25077                 continue;
25078             }
25079             if (a.name == 'style') {
25080                 cleanStyle(a.name,a.value);
25081                 continue;
25082             }
25083             /// clean up MS crap..
25084             // tecnically this should be a list of valid class'es..
25085             
25086             
25087             if (a.name == 'class') {
25088                 if (a.value.match(/^Mso/)) {
25089                     node.removeAttribute('class');
25090                 }
25091                 
25092                 if (a.value.match(/^body$/)) {
25093                     node.removeAttribute('class');
25094                 }
25095                 continue;
25096             }
25097             
25098             // style cleanup!?
25099             // class cleanup?
25100             
25101         }
25102         
25103         
25104         this.cleanUpChildren(node);
25105         
25106         
25107     },
25108     
25109     /**
25110      * Clean up MS wordisms...
25111      */
25112     cleanWord : function(node)
25113     {
25114         if (!node) {
25115             this.cleanWord(this.doc.body);
25116             return;
25117         }
25118         
25119         if(
25120                 node.nodeName == 'SPAN' &&
25121                 !node.hasAttributes() &&
25122                 node.childNodes.length == 1 &&
25123                 node.firstChild.nodeName == "#text"  
25124         ) {
25125             var textNode = node.firstChild;
25126             node.removeChild(textNode);
25127             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25128                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25129             }
25130             node.parentNode.insertBefore(textNode, node);
25131             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25132                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25133             }
25134             node.parentNode.removeChild(node);
25135         }
25136         
25137         if (node.nodeName == "#text") {
25138             // clean up silly Windows -- stuff?
25139             return; 
25140         }
25141         if (node.nodeName == "#comment") {
25142             node.parentNode.removeChild(node);
25143             // clean up silly Windows -- stuff?
25144             return; 
25145         }
25146         
25147         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25148             node.parentNode.removeChild(node);
25149             return;
25150         }
25151         //Roo.log(node.tagName);
25152         // remove - but keep children..
25153         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25154             //Roo.log('-- removed');
25155             while (node.childNodes.length) {
25156                 var cn = node.childNodes[0];
25157                 node.removeChild(cn);
25158                 node.parentNode.insertBefore(cn, node);
25159                 // move node to parent - and clean it..
25160                 this.cleanWord(cn);
25161             }
25162             node.parentNode.removeChild(node);
25163             /// no need to iterate chidlren = it's got none..
25164             //this.iterateChildren(node, this.cleanWord);
25165             return;
25166         }
25167         // clean styles
25168         if (node.className.length) {
25169             
25170             var cn = node.className.split(/\W+/);
25171             var cna = [];
25172             Roo.each(cn, function(cls) {
25173                 if (cls.match(/Mso[a-zA-Z]+/)) {
25174                     return;
25175                 }
25176                 cna.push(cls);
25177             });
25178             node.className = cna.length ? cna.join(' ') : '';
25179             if (!cna.length) {
25180                 node.removeAttribute("class");
25181             }
25182         }
25183         
25184         if (node.hasAttribute("lang")) {
25185             node.removeAttribute("lang");
25186         }
25187         
25188         if (node.hasAttribute("style")) {
25189             
25190             var styles = node.getAttribute("style").split(";");
25191             var nstyle = [];
25192             Roo.each(styles, function(s) {
25193                 if (!s.match(/:/)) {
25194                     return;
25195                 }
25196                 var kv = s.split(":");
25197                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25198                     return;
25199                 }
25200                 // what ever is left... we allow.
25201                 nstyle.push(s);
25202             });
25203             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25204             if (!nstyle.length) {
25205                 node.removeAttribute('style');
25206             }
25207         }
25208         this.iterateChildren(node, this.cleanWord);
25209         
25210         
25211         
25212     },
25213     /**
25214      * iterateChildren of a Node, calling fn each time, using this as the scole..
25215      * @param {DomNode} node node to iterate children of.
25216      * @param {Function} fn method of this class to call on each item.
25217      */
25218     iterateChildren : function(node, fn)
25219     {
25220         if (!node.childNodes.length) {
25221                 return;
25222         }
25223         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25224            fn.call(this, node.childNodes[i])
25225         }
25226     },
25227     
25228     
25229     /**
25230      * cleanTableWidths.
25231      *
25232      * Quite often pasting from word etc.. results in tables with column and widths.
25233      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25234      *
25235      */
25236     cleanTableWidths : function(node)
25237     {
25238          
25239          
25240         if (!node) {
25241             this.cleanTableWidths(this.doc.body);
25242             return;
25243         }
25244         
25245         // ignore list...
25246         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25247             return; 
25248         }
25249         Roo.log(node.tagName);
25250         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25251             this.iterateChildren(node, this.cleanTableWidths);
25252             return;
25253         }
25254         if (node.hasAttribute('width')) {
25255             node.removeAttribute('width');
25256         }
25257         
25258          
25259         if (node.hasAttribute("style")) {
25260             // pretty basic...
25261             
25262             var styles = node.getAttribute("style").split(";");
25263             var nstyle = [];
25264             Roo.each(styles, function(s) {
25265                 if (!s.match(/:/)) {
25266                     return;
25267                 }
25268                 var kv = s.split(":");
25269                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25270                     return;
25271                 }
25272                 // what ever is left... we allow.
25273                 nstyle.push(s);
25274             });
25275             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25276             if (!nstyle.length) {
25277                 node.removeAttribute('style');
25278             }
25279         }
25280         
25281         this.iterateChildren(node, this.cleanTableWidths);
25282         
25283         
25284     },
25285     
25286     
25287     
25288     
25289     domToHTML : function(currentElement, depth, nopadtext) {
25290         
25291         depth = depth || 0;
25292         nopadtext = nopadtext || false;
25293     
25294         if (!currentElement) {
25295             return this.domToHTML(this.doc.body);
25296         }
25297         
25298         //Roo.log(currentElement);
25299         var j;
25300         var allText = false;
25301         var nodeName = currentElement.nodeName;
25302         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25303         
25304         if  (nodeName == '#text') {
25305             
25306             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25307         }
25308         
25309         
25310         var ret = '';
25311         if (nodeName != 'BODY') {
25312              
25313             var i = 0;
25314             // Prints the node tagName, such as <A>, <IMG>, etc
25315             if (tagName) {
25316                 var attr = [];
25317                 for(i = 0; i < currentElement.attributes.length;i++) {
25318                     // quoting?
25319                     var aname = currentElement.attributes.item(i).name;
25320                     if (!currentElement.attributes.item(i).value.length) {
25321                         continue;
25322                     }
25323                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25324                 }
25325                 
25326                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25327             } 
25328             else {
25329                 
25330                 // eack
25331             }
25332         } else {
25333             tagName = false;
25334         }
25335         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25336             return ret;
25337         }
25338         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25339             nopadtext = true;
25340         }
25341         
25342         
25343         // Traverse the tree
25344         i = 0;
25345         var currentElementChild = currentElement.childNodes.item(i);
25346         var allText = true;
25347         var innerHTML  = '';
25348         lastnode = '';
25349         while (currentElementChild) {
25350             // Formatting code (indent the tree so it looks nice on the screen)
25351             var nopad = nopadtext;
25352             if (lastnode == 'SPAN') {
25353                 nopad  = true;
25354             }
25355             // text
25356             if  (currentElementChild.nodeName == '#text') {
25357                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25358                 toadd = nopadtext ? toadd : toadd.trim();
25359                 if (!nopad && toadd.length > 80) {
25360                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25361                 }
25362                 innerHTML  += toadd;
25363                 
25364                 i++;
25365                 currentElementChild = currentElement.childNodes.item(i);
25366                 lastNode = '';
25367                 continue;
25368             }
25369             allText = false;
25370             
25371             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25372                 
25373             // Recursively traverse the tree structure of the child node
25374             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25375             lastnode = currentElementChild.nodeName;
25376             i++;
25377             currentElementChild=currentElement.childNodes.item(i);
25378         }
25379         
25380         ret += innerHTML;
25381         
25382         if (!allText) {
25383                 // The remaining code is mostly for formatting the tree
25384             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25385         }
25386         
25387         
25388         if (tagName) {
25389             ret+= "</"+tagName+">";
25390         }
25391         return ret;
25392         
25393     },
25394         
25395     applyBlacklists : function()
25396     {
25397         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25398         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25399         
25400         this.white = [];
25401         this.black = [];
25402         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25403             if (b.indexOf(tag) > -1) {
25404                 return;
25405             }
25406             this.white.push(tag);
25407             
25408         }, this);
25409         
25410         Roo.each(w, function(tag) {
25411             if (b.indexOf(tag) > -1) {
25412                 return;
25413             }
25414             if (this.white.indexOf(tag) > -1) {
25415                 return;
25416             }
25417             this.white.push(tag);
25418             
25419         }, this);
25420         
25421         
25422         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25423             if (w.indexOf(tag) > -1) {
25424                 return;
25425             }
25426             this.black.push(tag);
25427             
25428         }, this);
25429         
25430         Roo.each(b, function(tag) {
25431             if (w.indexOf(tag) > -1) {
25432                 return;
25433             }
25434             if (this.black.indexOf(tag) > -1) {
25435                 return;
25436             }
25437             this.black.push(tag);
25438             
25439         }, this);
25440         
25441         
25442         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25443         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25444         
25445         this.cwhite = [];
25446         this.cblack = [];
25447         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25448             if (b.indexOf(tag) > -1) {
25449                 return;
25450             }
25451             this.cwhite.push(tag);
25452             
25453         }, this);
25454         
25455         Roo.each(w, function(tag) {
25456             if (b.indexOf(tag) > -1) {
25457                 return;
25458             }
25459             if (this.cwhite.indexOf(tag) > -1) {
25460                 return;
25461             }
25462             this.cwhite.push(tag);
25463             
25464         }, this);
25465         
25466         
25467         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25468             if (w.indexOf(tag) > -1) {
25469                 return;
25470             }
25471             this.cblack.push(tag);
25472             
25473         }, this);
25474         
25475         Roo.each(b, function(tag) {
25476             if (w.indexOf(tag) > -1) {
25477                 return;
25478             }
25479             if (this.cblack.indexOf(tag) > -1) {
25480                 return;
25481             }
25482             this.cblack.push(tag);
25483             
25484         }, this);
25485     },
25486     
25487     setStylesheets : function(stylesheets)
25488     {
25489         if(typeof(stylesheets) == 'string'){
25490             Roo.get(this.iframe.contentDocument.head).createChild({
25491                 tag : 'link',
25492                 rel : 'stylesheet',
25493                 type : 'text/css',
25494                 href : stylesheets
25495             });
25496             
25497             return;
25498         }
25499         var _this = this;
25500      
25501         Roo.each(stylesheets, function(s) {
25502             if(!s.length){
25503                 return;
25504             }
25505             
25506             Roo.get(_this.iframe.contentDocument.head).createChild({
25507                 tag : 'link',
25508                 rel : 'stylesheet',
25509                 type : 'text/css',
25510                 href : s
25511             });
25512         });
25513
25514         
25515     },
25516     
25517     removeStylesheets : function()
25518     {
25519         var _this = this;
25520         
25521         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25522             s.remove();
25523         });
25524     },
25525     
25526     setStyle : function(style)
25527     {
25528         Roo.get(this.iframe.contentDocument.head).createChild({
25529             tag : 'style',
25530             type : 'text/css',
25531             html : style
25532         });
25533
25534         return;
25535     }
25536     
25537     // hide stuff that is not compatible
25538     /**
25539      * @event blur
25540      * @hide
25541      */
25542     /**
25543      * @event change
25544      * @hide
25545      */
25546     /**
25547      * @event focus
25548      * @hide
25549      */
25550     /**
25551      * @event specialkey
25552      * @hide
25553      */
25554     /**
25555      * @cfg {String} fieldClass @hide
25556      */
25557     /**
25558      * @cfg {String} focusClass @hide
25559      */
25560     /**
25561      * @cfg {String} autoCreate @hide
25562      */
25563     /**
25564      * @cfg {String} inputType @hide
25565      */
25566     /**
25567      * @cfg {String} invalidClass @hide
25568      */
25569     /**
25570      * @cfg {String} invalidText @hide
25571      */
25572     /**
25573      * @cfg {String} msgFx @hide
25574      */
25575     /**
25576      * @cfg {String} validateOnBlur @hide
25577      */
25578 });
25579
25580 Roo.HtmlEditorCore.white = [
25581         'area', 'br', 'img', 'input', 'hr', 'wbr',
25582         
25583        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25584        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25585        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25586        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25587        'table',   'ul',         'xmp', 
25588        
25589        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25590       'thead',   'tr', 
25591      
25592       'dir', 'menu', 'ol', 'ul', 'dl',
25593        
25594       'embed',  'object'
25595 ];
25596
25597
25598 Roo.HtmlEditorCore.black = [
25599     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25600         'applet', // 
25601         'base',   'basefont', 'bgsound', 'blink',  'body', 
25602         'frame',  'frameset', 'head',    'html',   'ilayer', 
25603         'iframe', 'layer',  'link',     'meta',    'object',   
25604         'script', 'style' ,'title',  'xml' // clean later..
25605 ];
25606 Roo.HtmlEditorCore.clean = [
25607     'script', 'style', 'title', 'xml'
25608 ];
25609 Roo.HtmlEditorCore.remove = [
25610     'font'
25611 ];
25612 // attributes..
25613
25614 Roo.HtmlEditorCore.ablack = [
25615     'on'
25616 ];
25617     
25618 Roo.HtmlEditorCore.aclean = [ 
25619     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25620 ];
25621
25622 // protocols..
25623 Roo.HtmlEditorCore.pwhite= [
25624         'http',  'https',  'mailto'
25625 ];
25626
25627 // white listed style attributes.
25628 Roo.HtmlEditorCore.cwhite= [
25629       //  'text-align', /// default is to allow most things..
25630       
25631          
25632 //        'font-size'//??
25633 ];
25634
25635 // black listed style attributes.
25636 Roo.HtmlEditorCore.cblack= [
25637       //  'font-size' -- this can be set by the project 
25638 ];
25639
25640
25641 Roo.HtmlEditorCore.swapCodes   =[ 
25642     [    8211, "--" ], 
25643     [    8212, "--" ], 
25644     [    8216,  "'" ],  
25645     [    8217, "'" ],  
25646     [    8220, '"' ],  
25647     [    8221, '"' ],  
25648     [    8226, "*" ],  
25649     [    8230, "..." ]
25650 ]; 
25651
25652     /*
25653  * - LGPL
25654  *
25655  * HtmlEditor
25656  * 
25657  */
25658
25659 /**
25660  * @class Roo.bootstrap.HtmlEditor
25661  * @extends Roo.bootstrap.TextArea
25662  * Bootstrap HtmlEditor class
25663
25664  * @constructor
25665  * Create a new HtmlEditor
25666  * @param {Object} config The config object
25667  */
25668
25669 Roo.bootstrap.HtmlEditor = function(config){
25670     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25671     if (!this.toolbars) {
25672         this.toolbars = [];
25673     }
25674     
25675     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25676     this.addEvents({
25677             /**
25678              * @event initialize
25679              * Fires when the editor is fully initialized (including the iframe)
25680              * @param {HtmlEditor} this
25681              */
25682             initialize: true,
25683             /**
25684              * @event activate
25685              * Fires when the editor is first receives the focus. Any insertion must wait
25686              * until after this event.
25687              * @param {HtmlEditor} this
25688              */
25689             activate: true,
25690              /**
25691              * @event beforesync
25692              * Fires before the textarea is updated with content from the editor iframe. Return false
25693              * to cancel the sync.
25694              * @param {HtmlEditor} this
25695              * @param {String} html
25696              */
25697             beforesync: true,
25698              /**
25699              * @event beforepush
25700              * Fires before the iframe editor is updated with content from the textarea. Return false
25701              * to cancel the push.
25702              * @param {HtmlEditor} this
25703              * @param {String} html
25704              */
25705             beforepush: true,
25706              /**
25707              * @event sync
25708              * Fires when the textarea is updated with content from the editor iframe.
25709              * @param {HtmlEditor} this
25710              * @param {String} html
25711              */
25712             sync: true,
25713              /**
25714              * @event push
25715              * Fires when the iframe editor is updated with content from the textarea.
25716              * @param {HtmlEditor} this
25717              * @param {String} html
25718              */
25719             push: true,
25720              /**
25721              * @event editmodechange
25722              * Fires when the editor switches edit modes
25723              * @param {HtmlEditor} this
25724              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25725              */
25726             editmodechange: true,
25727             /**
25728              * @event editorevent
25729              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25730              * @param {HtmlEditor} this
25731              */
25732             editorevent: true,
25733             /**
25734              * @event firstfocus
25735              * Fires when on first focus - needed by toolbars..
25736              * @param {HtmlEditor} this
25737              */
25738             firstfocus: true,
25739             /**
25740              * @event autosave
25741              * Auto save the htmlEditor value as a file into Events
25742              * @param {HtmlEditor} this
25743              */
25744             autosave: true,
25745             /**
25746              * @event savedpreview
25747              * preview the saved version of htmlEditor
25748              * @param {HtmlEditor} this
25749              */
25750             savedpreview: true
25751         });
25752 };
25753
25754
25755 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25756     
25757     
25758       /**
25759      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25760      */
25761     toolbars : false,
25762     
25763      /**
25764     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25765     */
25766     btns : [],
25767    
25768      /**
25769      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25770      *                        Roo.resizable.
25771      */
25772     resizable : false,
25773      /**
25774      * @cfg {Number} height (in pixels)
25775      */   
25776     height: 300,
25777    /**
25778      * @cfg {Number} width (in pixels)
25779      */   
25780     width: false,
25781     
25782     /**
25783      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25784      * 
25785      */
25786     stylesheets: false,
25787     
25788     // id of frame..
25789     frameId: false,
25790     
25791     // private properties
25792     validationEvent : false,
25793     deferHeight: true,
25794     initialized : false,
25795     activated : false,
25796     
25797     onFocus : Roo.emptyFn,
25798     iframePad:3,
25799     hideMode:'offsets',
25800     
25801     tbContainer : false,
25802     
25803     bodyCls : '',
25804     
25805     toolbarContainer :function() {
25806         return this.wrap.select('.x-html-editor-tb',true).first();
25807     },
25808
25809     /**
25810      * Protected method that will not generally be called directly. It
25811      * is called when the editor creates its toolbar. Override this method if you need to
25812      * add custom toolbar buttons.
25813      * @param {HtmlEditor} editor
25814      */
25815     createToolbar : function(){
25816         Roo.log('renewing');
25817         Roo.log("create toolbars");
25818         
25819         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25820         this.toolbars[0].render(this.toolbarContainer());
25821         
25822         return;
25823         
25824 //        if (!editor.toolbars || !editor.toolbars.length) {
25825 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25826 //        }
25827 //        
25828 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25829 //            editor.toolbars[i] = Roo.factory(
25830 //                    typeof(editor.toolbars[i]) == 'string' ?
25831 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25832 //                Roo.bootstrap.HtmlEditor);
25833 //            editor.toolbars[i].init(editor);
25834 //        }
25835     },
25836
25837      
25838     // private
25839     onRender : function(ct, position)
25840     {
25841        // Roo.log("Call onRender: " + this.xtype);
25842         var _t = this;
25843         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25844       
25845         this.wrap = this.inputEl().wrap({
25846             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25847         });
25848         
25849         this.editorcore.onRender(ct, position);
25850          
25851         if (this.resizable) {
25852             this.resizeEl = new Roo.Resizable(this.wrap, {
25853                 pinned : true,
25854                 wrap: true,
25855                 dynamic : true,
25856                 minHeight : this.height,
25857                 height: this.height,
25858                 handles : this.resizable,
25859                 width: this.width,
25860                 listeners : {
25861                     resize : function(r, w, h) {
25862                         _t.onResize(w,h); // -something
25863                     }
25864                 }
25865             });
25866             
25867         }
25868         this.createToolbar(this);
25869        
25870         
25871         if(!this.width && this.resizable){
25872             this.setSize(this.wrap.getSize());
25873         }
25874         if (this.resizeEl) {
25875             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25876             // should trigger onReize..
25877         }
25878         
25879     },
25880
25881     // private
25882     onResize : function(w, h)
25883     {
25884         Roo.log('resize: ' +w + ',' + h );
25885         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25886         var ew = false;
25887         var eh = false;
25888         
25889         if(this.inputEl() ){
25890             if(typeof w == 'number'){
25891                 var aw = w - this.wrap.getFrameWidth('lr');
25892                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25893                 ew = aw;
25894             }
25895             if(typeof h == 'number'){
25896                  var tbh = -11;  // fixme it needs to tool bar size!
25897                 for (var i =0; i < this.toolbars.length;i++) {
25898                     // fixme - ask toolbars for heights?
25899                     tbh += this.toolbars[i].el.getHeight();
25900                     //if (this.toolbars[i].footer) {
25901                     //    tbh += this.toolbars[i].footer.el.getHeight();
25902                     //}
25903                 }
25904               
25905                 
25906                 
25907                 
25908                 
25909                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25910                 ah -= 5; // knock a few pixes off for look..
25911                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25912                 var eh = ah;
25913             }
25914         }
25915         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25916         this.editorcore.onResize(ew,eh);
25917         
25918     },
25919
25920     /**
25921      * Toggles the editor between standard and source edit mode.
25922      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25923      */
25924     toggleSourceEdit : function(sourceEditMode)
25925     {
25926         this.editorcore.toggleSourceEdit(sourceEditMode);
25927         
25928         if(this.editorcore.sourceEditMode){
25929             Roo.log('editor - showing textarea');
25930             
25931 //            Roo.log('in');
25932 //            Roo.log(this.syncValue());
25933             this.syncValue();
25934             this.inputEl().removeClass(['hide', 'x-hidden']);
25935             this.inputEl().dom.removeAttribute('tabIndex');
25936             this.inputEl().focus();
25937         }else{
25938             Roo.log('editor - hiding textarea');
25939 //            Roo.log('out')
25940 //            Roo.log(this.pushValue()); 
25941             this.pushValue();
25942             
25943             this.inputEl().addClass(['hide', 'x-hidden']);
25944             this.inputEl().dom.setAttribute('tabIndex', -1);
25945             //this.deferFocus();
25946         }
25947          
25948         if(this.resizable){
25949             this.setSize(this.wrap.getSize());
25950         }
25951         
25952         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25953     },
25954  
25955     // private (for BoxComponent)
25956     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25957
25958     // private (for BoxComponent)
25959     getResizeEl : function(){
25960         return this.wrap;
25961     },
25962
25963     // private (for BoxComponent)
25964     getPositionEl : function(){
25965         return this.wrap;
25966     },
25967
25968     // private
25969     initEvents : function(){
25970         this.originalValue = this.getValue();
25971     },
25972
25973 //    /**
25974 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25975 //     * @method
25976 //     */
25977 //    markInvalid : Roo.emptyFn,
25978 //    /**
25979 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25980 //     * @method
25981 //     */
25982 //    clearInvalid : Roo.emptyFn,
25983
25984     setValue : function(v){
25985         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25986         this.editorcore.pushValue();
25987     },
25988
25989      
25990     // private
25991     deferFocus : function(){
25992         this.focus.defer(10, this);
25993     },
25994
25995     // doc'ed in Field
25996     focus : function(){
25997         this.editorcore.focus();
25998         
25999     },
26000       
26001
26002     // private
26003     onDestroy : function(){
26004         
26005         
26006         
26007         if(this.rendered){
26008             
26009             for (var i =0; i < this.toolbars.length;i++) {
26010                 // fixme - ask toolbars for heights?
26011                 this.toolbars[i].onDestroy();
26012             }
26013             
26014             this.wrap.dom.innerHTML = '';
26015             this.wrap.remove();
26016         }
26017     },
26018
26019     // private
26020     onFirstFocus : function(){
26021         //Roo.log("onFirstFocus");
26022         this.editorcore.onFirstFocus();
26023          for (var i =0; i < this.toolbars.length;i++) {
26024             this.toolbars[i].onFirstFocus();
26025         }
26026         
26027     },
26028     
26029     // private
26030     syncValue : function()
26031     {   
26032         this.editorcore.syncValue();
26033     },
26034     
26035     pushValue : function()
26036     {   
26037         this.editorcore.pushValue();
26038     }
26039      
26040     
26041     // hide stuff that is not compatible
26042     /**
26043      * @event blur
26044      * @hide
26045      */
26046     /**
26047      * @event change
26048      * @hide
26049      */
26050     /**
26051      * @event focus
26052      * @hide
26053      */
26054     /**
26055      * @event specialkey
26056      * @hide
26057      */
26058     /**
26059      * @cfg {String} fieldClass @hide
26060      */
26061     /**
26062      * @cfg {String} focusClass @hide
26063      */
26064     /**
26065      * @cfg {String} autoCreate @hide
26066      */
26067     /**
26068      * @cfg {String} inputType @hide
26069      */
26070      
26071     /**
26072      * @cfg {String} invalidText @hide
26073      */
26074     /**
26075      * @cfg {String} msgFx @hide
26076      */
26077     /**
26078      * @cfg {String} validateOnBlur @hide
26079      */
26080 });
26081  
26082     
26083    
26084    
26085    
26086       
26087 Roo.namespace('Roo.bootstrap.htmleditor');
26088 /**
26089  * @class Roo.bootstrap.HtmlEditorToolbar1
26090  * Basic Toolbar
26091  * 
26092  * @example
26093  * Usage:
26094  *
26095  new Roo.bootstrap.HtmlEditor({
26096     ....
26097     toolbars : [
26098         new Roo.bootstrap.HtmlEditorToolbar1({
26099             disable : { fonts: 1 , format: 1, ..., ... , ...],
26100             btns : [ .... ]
26101         })
26102     }
26103      
26104  * 
26105  * @cfg {Object} disable List of elements to disable..
26106  * @cfg {Array} btns List of additional buttons.
26107  * 
26108  * 
26109  * NEEDS Extra CSS? 
26110  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26111  */
26112  
26113 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26114 {
26115     
26116     Roo.apply(this, config);
26117     
26118     // default disabled, based on 'good practice'..
26119     this.disable = this.disable || {};
26120     Roo.applyIf(this.disable, {
26121         fontSize : true,
26122         colors : true,
26123         specialElements : true
26124     });
26125     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26126     
26127     this.editor = config.editor;
26128     this.editorcore = config.editor.editorcore;
26129     
26130     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26131     
26132     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26133     // dont call parent... till later.
26134 }
26135 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26136      
26137     bar : true,
26138     
26139     editor : false,
26140     editorcore : false,
26141     
26142     
26143     formats : [
26144         "p" ,  
26145         "h1","h2","h3","h4","h5","h6", 
26146         "pre", "code", 
26147         "abbr", "acronym", "address", "cite", "samp", "var",
26148         'div','span'
26149     ],
26150     
26151     onRender : function(ct, position)
26152     {
26153        // Roo.log("Call onRender: " + this.xtype);
26154         
26155        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26156        Roo.log(this.el);
26157        this.el.dom.style.marginBottom = '0';
26158        var _this = this;
26159        var editorcore = this.editorcore;
26160        var editor= this.editor;
26161        
26162        var children = [];
26163        var btn = function(id,cmd , toggle, handler, html){
26164        
26165             var  event = toggle ? 'toggle' : 'click';
26166        
26167             var a = {
26168                 size : 'sm',
26169                 xtype: 'Button',
26170                 xns: Roo.bootstrap,
26171                 //glyphicon : id,
26172                 fa: id,
26173                 cmd : id || cmd,
26174                 enableToggle:toggle !== false,
26175                 html : html || '',
26176                 pressed : toggle ? false : null,
26177                 listeners : {}
26178             };
26179             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26180                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26181             };
26182             children.push(a);
26183             return a;
26184        }
26185        
26186     //    var cb_box = function...
26187         
26188         var style = {
26189                 xtype: 'Button',
26190                 size : 'sm',
26191                 xns: Roo.bootstrap,
26192                 fa : 'font',
26193                 //html : 'submit'
26194                 menu : {
26195                     xtype: 'Menu',
26196                     xns: Roo.bootstrap,
26197                     items:  []
26198                 }
26199         };
26200         Roo.each(this.formats, function(f) {
26201             style.menu.items.push({
26202                 xtype :'MenuItem',
26203                 xns: Roo.bootstrap,
26204                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26205                 tagname : f,
26206                 listeners : {
26207                     click : function()
26208                     {
26209                         editorcore.insertTag(this.tagname);
26210                         editor.focus();
26211                     }
26212                 }
26213                 
26214             });
26215         });
26216         children.push(style);   
26217         
26218         btn('bold',false,true);
26219         btn('italic',false,true);
26220         btn('align-left', 'justifyleft',true);
26221         btn('align-center', 'justifycenter',true);
26222         btn('align-right' , 'justifyright',true);
26223         btn('link', false, false, function(btn) {
26224             //Roo.log("create link?");
26225             var url = prompt(this.createLinkText, this.defaultLinkValue);
26226             if(url && url != 'http:/'+'/'){
26227                 this.editorcore.relayCmd('createlink', url);
26228             }
26229         }),
26230         btn('list','insertunorderedlist',true);
26231         btn('pencil', false,true, function(btn){
26232                 Roo.log(this);
26233                 this.toggleSourceEdit(btn.pressed);
26234         });
26235         
26236         if (this.editor.btns.length > 0) {
26237             for (var i = 0; i<this.editor.btns.length; i++) {
26238                 children.push(this.editor.btns[i]);
26239             }
26240         }
26241         
26242         /*
26243         var cog = {
26244                 xtype: 'Button',
26245                 size : 'sm',
26246                 xns: Roo.bootstrap,
26247                 glyphicon : 'cog',
26248                 //html : 'submit'
26249                 menu : {
26250                     xtype: 'Menu',
26251                     xns: Roo.bootstrap,
26252                     items:  []
26253                 }
26254         };
26255         
26256         cog.menu.items.push({
26257             xtype :'MenuItem',
26258             xns: Roo.bootstrap,
26259             html : Clean styles,
26260             tagname : f,
26261             listeners : {
26262                 click : function()
26263                 {
26264                     editorcore.insertTag(this.tagname);
26265                     editor.focus();
26266                 }
26267             }
26268             
26269         });
26270        */
26271         
26272          
26273        this.xtype = 'NavSimplebar';
26274         
26275         for(var i=0;i< children.length;i++) {
26276             
26277             this.buttons.add(this.addxtypeChild(children[i]));
26278             
26279         }
26280         
26281         editor.on('editorevent', this.updateToolbar, this);
26282     },
26283     onBtnClick : function(id)
26284     {
26285        this.editorcore.relayCmd(id);
26286        this.editorcore.focus();
26287     },
26288     
26289     /**
26290      * Protected method that will not generally be called directly. It triggers
26291      * a toolbar update by reading the markup state of the current selection in the editor.
26292      */
26293     updateToolbar: function(){
26294
26295         if(!this.editorcore.activated){
26296             this.editor.onFirstFocus(); // is this neeed?
26297             return;
26298         }
26299
26300         var btns = this.buttons; 
26301         var doc = this.editorcore.doc;
26302         btns.get('bold').setActive(doc.queryCommandState('bold'));
26303         btns.get('italic').setActive(doc.queryCommandState('italic'));
26304         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26305         
26306         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26307         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26308         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26309         
26310         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26311         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26312          /*
26313         
26314         var ans = this.editorcore.getAllAncestors();
26315         if (this.formatCombo) {
26316             
26317             
26318             var store = this.formatCombo.store;
26319             this.formatCombo.setValue("");
26320             for (var i =0; i < ans.length;i++) {
26321                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26322                     // select it..
26323                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26324                     break;
26325                 }
26326             }
26327         }
26328         
26329         
26330         
26331         // hides menus... - so this cant be on a menu...
26332         Roo.bootstrap.MenuMgr.hideAll();
26333         */
26334         Roo.bootstrap.MenuMgr.hideAll();
26335         //this.editorsyncValue();
26336     },
26337     onFirstFocus: function() {
26338         this.buttons.each(function(item){
26339            item.enable();
26340         });
26341     },
26342     toggleSourceEdit : function(sourceEditMode){
26343         
26344           
26345         if(sourceEditMode){
26346             Roo.log("disabling buttons");
26347            this.buttons.each( function(item){
26348                 if(item.cmd != 'pencil'){
26349                     item.disable();
26350                 }
26351             });
26352           
26353         }else{
26354             Roo.log("enabling buttons");
26355             if(this.editorcore.initialized){
26356                 this.buttons.each( function(item){
26357                     item.enable();
26358                 });
26359             }
26360             
26361         }
26362         Roo.log("calling toggole on editor");
26363         // tell the editor that it's been pressed..
26364         this.editor.toggleSourceEdit(sourceEditMode);
26365        
26366     }
26367 });
26368
26369
26370
26371
26372  
26373 /*
26374  * - LGPL
26375  */
26376
26377 /**
26378  * @class Roo.bootstrap.Markdown
26379  * @extends Roo.bootstrap.TextArea
26380  * Bootstrap Showdown editable area
26381  * @cfg {string} content
26382  * 
26383  * @constructor
26384  * Create a new Showdown
26385  */
26386
26387 Roo.bootstrap.Markdown = function(config){
26388     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26389    
26390 };
26391
26392 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26393     
26394     editing :false,
26395     
26396     initEvents : function()
26397     {
26398         
26399         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26400         this.markdownEl = this.el.createChild({
26401             cls : 'roo-markdown-area'
26402         });
26403         this.inputEl().addClass('d-none');
26404         if (this.getValue() == '') {
26405             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26406             
26407         } else {
26408             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26409         }
26410         this.markdownEl.on('click', this.toggleTextEdit, this);
26411         this.on('blur', this.toggleTextEdit, this);
26412         this.on('specialkey', this.resizeTextArea, this);
26413     },
26414     
26415     toggleTextEdit : function()
26416     {
26417         var sh = this.markdownEl.getHeight();
26418         this.inputEl().addClass('d-none');
26419         this.markdownEl.addClass('d-none');
26420         if (!this.editing) {
26421             // show editor?
26422             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26423             this.inputEl().removeClass('d-none');
26424             this.inputEl().focus();
26425             this.editing = true;
26426             return;
26427         }
26428         // show showdown...
26429         this.updateMarkdown();
26430         this.markdownEl.removeClass('d-none');
26431         this.editing = false;
26432         return;
26433     },
26434     updateMarkdown : function()
26435     {
26436         if (this.getValue() == '') {
26437             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26438             return;
26439         }
26440  
26441         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26442     },
26443     
26444     resizeTextArea: function () {
26445         
26446         var sh = 100;
26447         Roo.log([sh, this.getValue().split("\n").length * 30]);
26448         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26449     },
26450     setValue : function(val)
26451     {
26452         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26453         if (!this.editing) {
26454             this.updateMarkdown();
26455         }
26456         
26457     },
26458     focus : function()
26459     {
26460         if (!this.editing) {
26461             this.toggleTextEdit();
26462         }
26463         
26464     }
26465
26466
26467 });
26468 /**
26469  * @class Roo.bootstrap.Table.AbstractSelectionModel
26470  * @extends Roo.util.Observable
26471  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26472  * implemented by descendant classes.  This class should not be directly instantiated.
26473  * @constructor
26474  */
26475 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26476     this.locked = false;
26477     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26478 };
26479
26480
26481 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26482     /** @ignore Called by the grid automatically. Do not call directly. */
26483     init : function(grid){
26484         this.grid = grid;
26485         this.initEvents();
26486     },
26487
26488     /**
26489      * Locks the selections.
26490      */
26491     lock : function(){
26492         this.locked = true;
26493     },
26494
26495     /**
26496      * Unlocks the selections.
26497      */
26498     unlock : function(){
26499         this.locked = false;
26500     },
26501
26502     /**
26503      * Returns true if the selections are locked.
26504      * @return {Boolean}
26505      */
26506     isLocked : function(){
26507         return this.locked;
26508     },
26509     
26510     
26511     initEvents : function ()
26512     {
26513         
26514     }
26515 });
26516 /**
26517  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26518  * @class Roo.bootstrap.Table.RowSelectionModel
26519  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26520  * It supports multiple selections and keyboard selection/navigation. 
26521  * @constructor
26522  * @param {Object} config
26523  */
26524
26525 Roo.bootstrap.Table.RowSelectionModel = function(config){
26526     Roo.apply(this, config);
26527     this.selections = new Roo.util.MixedCollection(false, function(o){
26528         return o.id;
26529     });
26530
26531     this.last = false;
26532     this.lastActive = false;
26533
26534     this.addEvents({
26535         /**
26536              * @event selectionchange
26537              * Fires when the selection changes
26538              * @param {SelectionModel} this
26539              */
26540             "selectionchange" : true,
26541         /**
26542              * @event afterselectionchange
26543              * Fires after the selection changes (eg. by key press or clicking)
26544              * @param {SelectionModel} this
26545              */
26546             "afterselectionchange" : true,
26547         /**
26548              * @event beforerowselect
26549              * Fires when a row is selected being selected, return false to cancel.
26550              * @param {SelectionModel} this
26551              * @param {Number} rowIndex The selected index
26552              * @param {Boolean} keepExisting False if other selections will be cleared
26553              */
26554             "beforerowselect" : true,
26555         /**
26556              * @event rowselect
26557              * Fires when a row is selected.
26558              * @param {SelectionModel} this
26559              * @param {Number} rowIndex The selected index
26560              * @param {Roo.data.Record} r The record
26561              */
26562             "rowselect" : true,
26563         /**
26564              * @event rowdeselect
26565              * Fires when a row is deselected.
26566              * @param {SelectionModel} this
26567              * @param {Number} rowIndex The selected index
26568              */
26569         "rowdeselect" : true
26570     });
26571     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26572     this.locked = false;
26573  };
26574
26575 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26576     /**
26577      * @cfg {Boolean} singleSelect
26578      * True to allow selection of only one row at a time (defaults to false)
26579      */
26580     singleSelect : false,
26581
26582     // private
26583     initEvents : function()
26584     {
26585
26586         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26587         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26588         //}else{ // allow click to work like normal
26589          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26590         //}
26591         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26592         this.grid.on("rowclick", this.handleMouseDown, this);
26593         
26594         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26595             "up" : function(e){
26596                 if(!e.shiftKey){
26597                     this.selectPrevious(e.shiftKey);
26598                 }else if(this.last !== false && this.lastActive !== false){
26599                     var last = this.last;
26600                     this.selectRange(this.last,  this.lastActive-1);
26601                     this.grid.getView().focusRow(this.lastActive);
26602                     if(last !== false){
26603                         this.last = last;
26604                     }
26605                 }else{
26606                     this.selectFirstRow();
26607                 }
26608                 this.fireEvent("afterselectionchange", this);
26609             },
26610             "down" : function(e){
26611                 if(!e.shiftKey){
26612                     this.selectNext(e.shiftKey);
26613                 }else if(this.last !== false && this.lastActive !== false){
26614                     var last = this.last;
26615                     this.selectRange(this.last,  this.lastActive+1);
26616                     this.grid.getView().focusRow(this.lastActive);
26617                     if(last !== false){
26618                         this.last = last;
26619                     }
26620                 }else{
26621                     this.selectFirstRow();
26622                 }
26623                 this.fireEvent("afterselectionchange", this);
26624             },
26625             scope: this
26626         });
26627         this.grid.store.on('load', function(){
26628             this.selections.clear();
26629         },this);
26630         /*
26631         var view = this.grid.view;
26632         view.on("refresh", this.onRefresh, this);
26633         view.on("rowupdated", this.onRowUpdated, this);
26634         view.on("rowremoved", this.onRemove, this);
26635         */
26636     },
26637
26638     // private
26639     onRefresh : function()
26640     {
26641         var ds = this.grid.store, i, v = this.grid.view;
26642         var s = this.selections;
26643         s.each(function(r){
26644             if((i = ds.indexOfId(r.id)) != -1){
26645                 v.onRowSelect(i);
26646             }else{
26647                 s.remove(r);
26648             }
26649         });
26650     },
26651
26652     // private
26653     onRemove : function(v, index, r){
26654         this.selections.remove(r);
26655     },
26656
26657     // private
26658     onRowUpdated : function(v, index, r){
26659         if(this.isSelected(r)){
26660             v.onRowSelect(index);
26661         }
26662     },
26663
26664     /**
26665      * Select records.
26666      * @param {Array} records The records to select
26667      * @param {Boolean} keepExisting (optional) True to keep existing selections
26668      */
26669     selectRecords : function(records, keepExisting)
26670     {
26671         if(!keepExisting){
26672             this.clearSelections();
26673         }
26674             var ds = this.grid.store;
26675         for(var i = 0, len = records.length; i < len; i++){
26676             this.selectRow(ds.indexOf(records[i]), true);
26677         }
26678     },
26679
26680     /**
26681      * Gets the number of selected rows.
26682      * @return {Number}
26683      */
26684     getCount : function(){
26685         return this.selections.length;
26686     },
26687
26688     /**
26689      * Selects the first row in the grid.
26690      */
26691     selectFirstRow : function(){
26692         this.selectRow(0);
26693     },
26694
26695     /**
26696      * Select the last row.
26697      * @param {Boolean} keepExisting (optional) True to keep existing selections
26698      */
26699     selectLastRow : function(keepExisting){
26700         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26701         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26702     },
26703
26704     /**
26705      * Selects the row immediately following the last selected row.
26706      * @param {Boolean} keepExisting (optional) True to keep existing selections
26707      */
26708     selectNext : function(keepExisting)
26709     {
26710             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26711             this.selectRow(this.last+1, keepExisting);
26712             this.grid.getView().focusRow(this.last);
26713         }
26714     },
26715
26716     /**
26717      * Selects the row that precedes the last selected row.
26718      * @param {Boolean} keepExisting (optional) True to keep existing selections
26719      */
26720     selectPrevious : function(keepExisting){
26721         if(this.last){
26722             this.selectRow(this.last-1, keepExisting);
26723             this.grid.getView().focusRow(this.last);
26724         }
26725     },
26726
26727     /**
26728      * Returns the selected records
26729      * @return {Array} Array of selected records
26730      */
26731     getSelections : function(){
26732         return [].concat(this.selections.items);
26733     },
26734
26735     /**
26736      * Returns the first selected record.
26737      * @return {Record}
26738      */
26739     getSelected : function(){
26740         return this.selections.itemAt(0);
26741     },
26742
26743
26744     /**
26745      * Clears all selections.
26746      */
26747     clearSelections : function(fast)
26748     {
26749         if(this.locked) {
26750             return;
26751         }
26752         if(fast !== true){
26753                 var ds = this.grid.store;
26754             var s = this.selections;
26755             s.each(function(r){
26756                 this.deselectRow(ds.indexOfId(r.id));
26757             }, this);
26758             s.clear();
26759         }else{
26760             this.selections.clear();
26761         }
26762         this.last = false;
26763     },
26764
26765
26766     /**
26767      * Selects all rows.
26768      */
26769     selectAll : function(){
26770         if(this.locked) {
26771             return;
26772         }
26773         this.selections.clear();
26774         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26775             this.selectRow(i, true);
26776         }
26777     },
26778
26779     /**
26780      * Returns True if there is a selection.
26781      * @return {Boolean}
26782      */
26783     hasSelection : function(){
26784         return this.selections.length > 0;
26785     },
26786
26787     /**
26788      * Returns True if the specified row is selected.
26789      * @param {Number/Record} record The record or index of the record to check
26790      * @return {Boolean}
26791      */
26792     isSelected : function(index){
26793             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26794         return (r && this.selections.key(r.id) ? true : false);
26795     },
26796
26797     /**
26798      * Returns True if the specified record id is selected.
26799      * @param {String} id The id of record to check
26800      * @return {Boolean}
26801      */
26802     isIdSelected : function(id){
26803         return (this.selections.key(id) ? true : false);
26804     },
26805
26806
26807     // private
26808     handleMouseDBClick : function(e, t){
26809         
26810     },
26811     // private
26812     handleMouseDown : function(e, t)
26813     {
26814             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26815         if(this.isLocked() || rowIndex < 0 ){
26816             return;
26817         };
26818         if(e.shiftKey && this.last !== false){
26819             var last = this.last;
26820             this.selectRange(last, rowIndex, e.ctrlKey);
26821             this.last = last; // reset the last
26822             t.focus();
26823     
26824         }else{
26825             var isSelected = this.isSelected(rowIndex);
26826             //Roo.log("select row:" + rowIndex);
26827             if(isSelected){
26828                 this.deselectRow(rowIndex);
26829             } else {
26830                         this.selectRow(rowIndex, true);
26831             }
26832     
26833             /*
26834                 if(e.button !== 0 && isSelected){
26835                 alert('rowIndex 2: ' + rowIndex);
26836                     view.focusRow(rowIndex);
26837                 }else if(e.ctrlKey && isSelected){
26838                     this.deselectRow(rowIndex);
26839                 }else if(!isSelected){
26840                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26841                     view.focusRow(rowIndex);
26842                 }
26843             */
26844         }
26845         this.fireEvent("afterselectionchange", this);
26846     },
26847     // private
26848     handleDragableRowClick :  function(grid, rowIndex, e) 
26849     {
26850         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26851             this.selectRow(rowIndex, false);
26852             grid.view.focusRow(rowIndex);
26853              this.fireEvent("afterselectionchange", this);
26854         }
26855     },
26856     
26857     /**
26858      * Selects multiple rows.
26859      * @param {Array} rows Array of the indexes of the row to select
26860      * @param {Boolean} keepExisting (optional) True to keep existing selections
26861      */
26862     selectRows : function(rows, keepExisting){
26863         if(!keepExisting){
26864             this.clearSelections();
26865         }
26866         for(var i = 0, len = rows.length; i < len; i++){
26867             this.selectRow(rows[i], true);
26868         }
26869     },
26870
26871     /**
26872      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26873      * @param {Number} startRow The index of the first row in the range
26874      * @param {Number} endRow The index of the last row in the range
26875      * @param {Boolean} keepExisting (optional) True to retain existing selections
26876      */
26877     selectRange : function(startRow, endRow, keepExisting){
26878         if(this.locked) {
26879             return;
26880         }
26881         if(!keepExisting){
26882             this.clearSelections();
26883         }
26884         if(startRow <= endRow){
26885             for(var i = startRow; i <= endRow; i++){
26886                 this.selectRow(i, true);
26887             }
26888         }else{
26889             for(var i = startRow; i >= endRow; i--){
26890                 this.selectRow(i, true);
26891             }
26892         }
26893     },
26894
26895     /**
26896      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26897      * @param {Number} startRow The index of the first row in the range
26898      * @param {Number} endRow The index of the last row in the range
26899      */
26900     deselectRange : function(startRow, endRow, preventViewNotify){
26901         if(this.locked) {
26902             return;
26903         }
26904         for(var i = startRow; i <= endRow; i++){
26905             this.deselectRow(i, preventViewNotify);
26906         }
26907     },
26908
26909     /**
26910      * Selects a row.
26911      * @param {Number} row The index of the row to select
26912      * @param {Boolean} keepExisting (optional) True to keep existing selections
26913      */
26914     selectRow : function(index, keepExisting, preventViewNotify)
26915     {
26916             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26917             return;
26918         }
26919         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26920             if(!keepExisting || this.singleSelect){
26921                 this.clearSelections();
26922             }
26923             
26924             var r = this.grid.store.getAt(index);
26925             //console.log('selectRow - record id :' + r.id);
26926             
26927             this.selections.add(r);
26928             this.last = this.lastActive = index;
26929             if(!preventViewNotify){
26930                 var proxy = new Roo.Element(
26931                                 this.grid.getRowDom(index)
26932                 );
26933                 proxy.addClass('bg-info info');
26934             }
26935             this.fireEvent("rowselect", this, index, r);
26936             this.fireEvent("selectionchange", this);
26937         }
26938     },
26939
26940     /**
26941      * Deselects a row.
26942      * @param {Number} row The index of the row to deselect
26943      */
26944     deselectRow : function(index, preventViewNotify)
26945     {
26946         if(this.locked) {
26947             return;
26948         }
26949         if(this.last == index){
26950             this.last = false;
26951         }
26952         if(this.lastActive == index){
26953             this.lastActive = false;
26954         }
26955         
26956         var r = this.grid.store.getAt(index);
26957         if (!r) {
26958             return;
26959         }
26960         
26961         this.selections.remove(r);
26962         //.console.log('deselectRow - record id :' + r.id);
26963         if(!preventViewNotify){
26964         
26965             var proxy = new Roo.Element(
26966                 this.grid.getRowDom(index)
26967             );
26968             proxy.removeClass('bg-info info');
26969         }
26970         this.fireEvent("rowdeselect", this, index);
26971         this.fireEvent("selectionchange", this);
26972     },
26973
26974     // private
26975     restoreLast : function(){
26976         if(this._last){
26977             this.last = this._last;
26978         }
26979     },
26980
26981     // private
26982     acceptsNav : function(row, col, cm){
26983         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26984     },
26985
26986     // private
26987     onEditorKey : function(field, e){
26988         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26989         if(k == e.TAB){
26990             e.stopEvent();
26991             ed.completeEdit();
26992             if(e.shiftKey){
26993                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26994             }else{
26995                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26996             }
26997         }else if(k == e.ENTER && !e.ctrlKey){
26998             e.stopEvent();
26999             ed.completeEdit();
27000             if(e.shiftKey){
27001                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27002             }else{
27003                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27004             }
27005         }else if(k == e.ESC){
27006             ed.cancelEdit();
27007         }
27008         if(newCell){
27009             g.startEditing(newCell[0], newCell[1]);
27010         }
27011     }
27012 });
27013 /*
27014  * Based on:
27015  * Ext JS Library 1.1.1
27016  * Copyright(c) 2006-2007, Ext JS, LLC.
27017  *
27018  * Originally Released Under LGPL - original licence link has changed is not relivant.
27019  *
27020  * Fork - LGPL
27021  * <script type="text/javascript">
27022  */
27023  
27024 /**
27025  * @class Roo.bootstrap.PagingToolbar
27026  * @extends Roo.bootstrap.NavSimplebar
27027  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27028  * @constructor
27029  * Create a new PagingToolbar
27030  * @param {Object} config The config object
27031  * @param {Roo.data.Store} store
27032  */
27033 Roo.bootstrap.PagingToolbar = function(config)
27034 {
27035     // old args format still supported... - xtype is prefered..
27036         // created from xtype...
27037     
27038     this.ds = config.dataSource;
27039     
27040     if (config.store && !this.ds) {
27041         this.store= Roo.factory(config.store, Roo.data);
27042         this.ds = this.store;
27043         this.ds.xmodule = this.xmodule || false;
27044     }
27045     
27046     this.toolbarItems = [];
27047     if (config.items) {
27048         this.toolbarItems = config.items;
27049     }
27050     
27051     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27052     
27053     this.cursor = 0;
27054     
27055     if (this.ds) { 
27056         this.bind(this.ds);
27057     }
27058     
27059     if (Roo.bootstrap.version == 4) {
27060         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27061     } else {
27062         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27063     }
27064     
27065 };
27066
27067 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27068     /**
27069      * @cfg {Roo.data.Store} dataSource
27070      * The underlying data store providing the paged data
27071      */
27072     /**
27073      * @cfg {String/HTMLElement/Element} container
27074      * container The id or element that will contain the toolbar
27075      */
27076     /**
27077      * @cfg {Boolean} displayInfo
27078      * True to display the displayMsg (defaults to false)
27079      */
27080     /**
27081      * @cfg {Number} pageSize
27082      * The number of records to display per page (defaults to 20)
27083      */
27084     pageSize: 20,
27085     /**
27086      * @cfg {String} displayMsg
27087      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27088      */
27089     displayMsg : 'Displaying {0} - {1} of {2}',
27090     /**
27091      * @cfg {String} emptyMsg
27092      * The message to display when no records are found (defaults to "No data to display")
27093      */
27094     emptyMsg : 'No data to display',
27095     /**
27096      * Customizable piece of the default paging text (defaults to "Page")
27097      * @type String
27098      */
27099     beforePageText : "Page",
27100     /**
27101      * Customizable piece of the default paging text (defaults to "of %0")
27102      * @type String
27103      */
27104     afterPageText : "of {0}",
27105     /**
27106      * Customizable piece of the default paging text (defaults to "First Page")
27107      * @type String
27108      */
27109     firstText : "First Page",
27110     /**
27111      * Customizable piece of the default paging text (defaults to "Previous Page")
27112      * @type String
27113      */
27114     prevText : "Previous Page",
27115     /**
27116      * Customizable piece of the default paging text (defaults to "Next Page")
27117      * @type String
27118      */
27119     nextText : "Next Page",
27120     /**
27121      * Customizable piece of the default paging text (defaults to "Last Page")
27122      * @type String
27123      */
27124     lastText : "Last Page",
27125     /**
27126      * Customizable piece of the default paging text (defaults to "Refresh")
27127      * @type String
27128      */
27129     refreshText : "Refresh",
27130
27131     buttons : false,
27132     // private
27133     onRender : function(ct, position) 
27134     {
27135         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27136         this.navgroup.parentId = this.id;
27137         this.navgroup.onRender(this.el, null);
27138         // add the buttons to the navgroup
27139         
27140         if(this.displayInfo){
27141             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27142             this.displayEl = this.el.select('.x-paging-info', true).first();
27143 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27144 //            this.displayEl = navel.el.select('span',true).first();
27145         }
27146         
27147         var _this = this;
27148         
27149         if(this.buttons){
27150             Roo.each(_this.buttons, function(e){ // this might need to use render????
27151                Roo.factory(e).render(_this.el);
27152             });
27153         }
27154             
27155         Roo.each(_this.toolbarItems, function(e) {
27156             _this.navgroup.addItem(e);
27157         });
27158         
27159         
27160         this.first = this.navgroup.addItem({
27161             tooltip: this.firstText,
27162             cls: "prev btn-outline-secondary",
27163             html : ' <i class="fa fa-step-backward"></i>',
27164             disabled: true,
27165             preventDefault: true,
27166             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27167         });
27168         
27169         this.prev =  this.navgroup.addItem({
27170             tooltip: this.prevText,
27171             cls: "prev btn-outline-secondary",
27172             html : ' <i class="fa fa-backward"></i>',
27173             disabled: true,
27174             preventDefault: true,
27175             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27176         });
27177     //this.addSeparator();
27178         
27179         
27180         var field = this.navgroup.addItem( {
27181             tagtype : 'span',
27182             cls : 'x-paging-position  btn-outline-secondary',
27183              disabled: true,
27184             html : this.beforePageText  +
27185                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27186                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27187          } ); //?? escaped?
27188         
27189         this.field = field.el.select('input', true).first();
27190         this.field.on("keydown", this.onPagingKeydown, this);
27191         this.field.on("focus", function(){this.dom.select();});
27192     
27193     
27194         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27195         //this.field.setHeight(18);
27196         //this.addSeparator();
27197         this.next = this.navgroup.addItem({
27198             tooltip: this.nextText,
27199             cls: "next btn-outline-secondary",
27200             html : ' <i class="fa fa-forward"></i>',
27201             disabled: true,
27202             preventDefault: true,
27203             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27204         });
27205         this.last = this.navgroup.addItem({
27206             tooltip: this.lastText,
27207             html : ' <i class="fa fa-step-forward"></i>',
27208             cls: "next btn-outline-secondary",
27209             disabled: true,
27210             preventDefault: true,
27211             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27212         });
27213     //this.addSeparator();
27214         this.loading = this.navgroup.addItem({
27215             tooltip: this.refreshText,
27216             cls: "btn-outline-secondary",
27217             html : ' <i class="fa fa-refresh"></i>',
27218             preventDefault: true,
27219             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27220         });
27221         
27222     },
27223
27224     // private
27225     updateInfo : function(){
27226         if(this.displayEl){
27227             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27228             var msg = count == 0 ?
27229                 this.emptyMsg :
27230                 String.format(
27231                     this.displayMsg,
27232                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27233                 );
27234             this.displayEl.update(msg);
27235         }
27236     },
27237
27238     // private
27239     onLoad : function(ds, r, o)
27240     {
27241         this.cursor = o.params.start ? o.params.start : 0;
27242         
27243         var d = this.getPageData(),
27244             ap = d.activePage,
27245             ps = d.pages;
27246         
27247         
27248         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27249         this.field.dom.value = ap;
27250         this.first.setDisabled(ap == 1);
27251         this.prev.setDisabled(ap == 1);
27252         this.next.setDisabled(ap == ps);
27253         this.last.setDisabled(ap == ps);
27254         this.loading.enable();
27255         this.updateInfo();
27256     },
27257
27258     // private
27259     getPageData : function(){
27260         var total = this.ds.getTotalCount();
27261         return {
27262             total : total,
27263             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27264             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27265         };
27266     },
27267
27268     // private
27269     onLoadError : function(){
27270         this.loading.enable();
27271     },
27272
27273     // private
27274     onPagingKeydown : function(e){
27275         var k = e.getKey();
27276         var d = this.getPageData();
27277         if(k == e.RETURN){
27278             var v = this.field.dom.value, pageNum;
27279             if(!v || isNaN(pageNum = parseInt(v, 10))){
27280                 this.field.dom.value = d.activePage;
27281                 return;
27282             }
27283             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27284             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27285             e.stopEvent();
27286         }
27287         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))
27288         {
27289           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27290           this.field.dom.value = pageNum;
27291           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27292           e.stopEvent();
27293         }
27294         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27295         {
27296           var v = this.field.dom.value, pageNum; 
27297           var increment = (e.shiftKey) ? 10 : 1;
27298           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27299                 increment *= -1;
27300           }
27301           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27302             this.field.dom.value = d.activePage;
27303             return;
27304           }
27305           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27306           {
27307             this.field.dom.value = parseInt(v, 10) + increment;
27308             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27309             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27310           }
27311           e.stopEvent();
27312         }
27313     },
27314
27315     // private
27316     beforeLoad : function(){
27317         if(this.loading){
27318             this.loading.disable();
27319         }
27320     },
27321
27322     // private
27323     onClick : function(which){
27324         
27325         var ds = this.ds;
27326         if (!ds) {
27327             return;
27328         }
27329         
27330         switch(which){
27331             case "first":
27332                 ds.load({params:{start: 0, limit: this.pageSize}});
27333             break;
27334             case "prev":
27335                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27336             break;
27337             case "next":
27338                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27339             break;
27340             case "last":
27341                 var total = ds.getTotalCount();
27342                 var extra = total % this.pageSize;
27343                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27344                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27345             break;
27346             case "refresh":
27347                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27348             break;
27349         }
27350     },
27351
27352     /**
27353      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27354      * @param {Roo.data.Store} store The data store to unbind
27355      */
27356     unbind : function(ds){
27357         ds.un("beforeload", this.beforeLoad, this);
27358         ds.un("load", this.onLoad, this);
27359         ds.un("loadexception", this.onLoadError, this);
27360         ds.un("remove", this.updateInfo, this);
27361         ds.un("add", this.updateInfo, this);
27362         this.ds = undefined;
27363     },
27364
27365     /**
27366      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27367      * @param {Roo.data.Store} store The data store to bind
27368      */
27369     bind : function(ds){
27370         ds.on("beforeload", this.beforeLoad, this);
27371         ds.on("load", this.onLoad, this);
27372         ds.on("loadexception", this.onLoadError, this);
27373         ds.on("remove", this.updateInfo, this);
27374         ds.on("add", this.updateInfo, this);
27375         this.ds = ds;
27376     }
27377 });/*
27378  * - LGPL
27379  *
27380  * element
27381  * 
27382  */
27383
27384 /**
27385  * @class Roo.bootstrap.MessageBar
27386  * @extends Roo.bootstrap.Component
27387  * Bootstrap MessageBar class
27388  * @cfg {String} html contents of the MessageBar
27389  * @cfg {String} weight (info | success | warning | danger) default info
27390  * @cfg {String} beforeClass insert the bar before the given class
27391  * @cfg {Boolean} closable (true | false) default false
27392  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27393  * 
27394  * @constructor
27395  * Create a new Element
27396  * @param {Object} config The config object
27397  */
27398
27399 Roo.bootstrap.MessageBar = function(config){
27400     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27401 };
27402
27403 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27404     
27405     html: '',
27406     weight: 'info',
27407     closable: false,
27408     fixed: false,
27409     beforeClass: 'bootstrap-sticky-wrap',
27410     
27411     getAutoCreate : function(){
27412         
27413         var cfg = {
27414             tag: 'div',
27415             cls: 'alert alert-dismissable alert-' + this.weight,
27416             cn: [
27417                 {
27418                     tag: 'span',
27419                     cls: 'message',
27420                     html: this.html || ''
27421                 }
27422             ]
27423         };
27424         
27425         if(this.fixed){
27426             cfg.cls += ' alert-messages-fixed';
27427         }
27428         
27429         if(this.closable){
27430             cfg.cn.push({
27431                 tag: 'button',
27432                 cls: 'close',
27433                 html: 'x'
27434             });
27435         }
27436         
27437         return cfg;
27438     },
27439     
27440     onRender : function(ct, position)
27441     {
27442         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27443         
27444         if(!this.el){
27445             var cfg = Roo.apply({},  this.getAutoCreate());
27446             cfg.id = Roo.id();
27447             
27448             if (this.cls) {
27449                 cfg.cls += ' ' + this.cls;
27450             }
27451             if (this.style) {
27452                 cfg.style = this.style;
27453             }
27454             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27455             
27456             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27457         }
27458         
27459         this.el.select('>button.close').on('click', this.hide, this);
27460         
27461     },
27462     
27463     show : function()
27464     {
27465         if (!this.rendered) {
27466             this.render();
27467         }
27468         
27469         this.el.show();
27470         
27471         this.fireEvent('show', this);
27472         
27473     },
27474     
27475     hide : function()
27476     {
27477         if (!this.rendered) {
27478             this.render();
27479         }
27480         
27481         this.el.hide();
27482         
27483         this.fireEvent('hide', this);
27484     },
27485     
27486     update : function()
27487     {
27488 //        var e = this.el.dom.firstChild;
27489 //        
27490 //        if(this.closable){
27491 //            e = e.nextSibling;
27492 //        }
27493 //        
27494 //        e.data = this.html || '';
27495
27496         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27497     }
27498    
27499 });
27500
27501  
27502
27503      /*
27504  * - LGPL
27505  *
27506  * Graph
27507  * 
27508  */
27509
27510
27511 /**
27512  * @class Roo.bootstrap.Graph
27513  * @extends Roo.bootstrap.Component
27514  * Bootstrap Graph class
27515 > Prameters
27516  -sm {number} sm 4
27517  -md {number} md 5
27518  @cfg {String} graphtype  bar | vbar | pie
27519  @cfg {number} g_x coodinator | centre x (pie)
27520  @cfg {number} g_y coodinator | centre y (pie)
27521  @cfg {number} g_r radius (pie)
27522  @cfg {number} g_height height of the chart (respected by all elements in the set)
27523  @cfg {number} g_width width of the chart (respected by all elements in the set)
27524  @cfg {Object} title The title of the chart
27525     
27526  -{Array}  values
27527  -opts (object) options for the chart 
27528      o {
27529      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27530      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27531      o vgutter (number)
27532      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.
27533      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27534      o to
27535      o stretch (boolean)
27536      o }
27537  -opts (object) options for the pie
27538      o{
27539      o cut
27540      o startAngle (number)
27541      o endAngle (number)
27542      } 
27543  *
27544  * @constructor
27545  * Create a new Input
27546  * @param {Object} config The config object
27547  */
27548
27549 Roo.bootstrap.Graph = function(config){
27550     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27551     
27552     this.addEvents({
27553         // img events
27554         /**
27555          * @event click
27556          * The img click event for the img.
27557          * @param {Roo.EventObject} e
27558          */
27559         "click" : true
27560     });
27561 };
27562
27563 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27564     
27565     sm: 4,
27566     md: 5,
27567     graphtype: 'bar',
27568     g_height: 250,
27569     g_width: 400,
27570     g_x: 50,
27571     g_y: 50,
27572     g_r: 30,
27573     opts:{
27574         //g_colors: this.colors,
27575         g_type: 'soft',
27576         g_gutter: '20%'
27577
27578     },
27579     title : false,
27580
27581     getAutoCreate : function(){
27582         
27583         var cfg = {
27584             tag: 'div',
27585             html : null
27586         };
27587         
27588         
27589         return  cfg;
27590     },
27591
27592     onRender : function(ct,position){
27593         
27594         
27595         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27596         
27597         if (typeof(Raphael) == 'undefined') {
27598             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27599             return;
27600         }
27601         
27602         this.raphael = Raphael(this.el.dom);
27603         
27604                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27605                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27606                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27607                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27608                 /*
27609                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27610                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27611                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27612                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27613                 
27614                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27615                 r.barchart(330, 10, 300, 220, data1);
27616                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27617                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27618                 */
27619                 
27620                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27621                 // r.barchart(30, 30, 560, 250,  xdata, {
27622                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27623                 //     axis : "0 0 1 1",
27624                 //     axisxlabels :  xdata
27625                 //     //yvalues : cols,
27626                    
27627                 // });
27628 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27629 //        
27630 //        this.load(null,xdata,{
27631 //                axis : "0 0 1 1",
27632 //                axisxlabels :  xdata
27633 //                });
27634
27635     },
27636
27637     load : function(graphtype,xdata,opts)
27638     {
27639         this.raphael.clear();
27640         if(!graphtype) {
27641             graphtype = this.graphtype;
27642         }
27643         if(!opts){
27644             opts = this.opts;
27645         }
27646         var r = this.raphael,
27647             fin = function () {
27648                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27649             },
27650             fout = function () {
27651                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27652             },
27653             pfin = function() {
27654                 this.sector.stop();
27655                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27656
27657                 if (this.label) {
27658                     this.label[0].stop();
27659                     this.label[0].attr({ r: 7.5 });
27660                     this.label[1].attr({ "font-weight": 800 });
27661                 }
27662             },
27663             pfout = function() {
27664                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27665
27666                 if (this.label) {
27667                     this.label[0].animate({ r: 5 }, 500, "bounce");
27668                     this.label[1].attr({ "font-weight": 400 });
27669                 }
27670             };
27671
27672         switch(graphtype){
27673             case 'bar':
27674                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27675                 break;
27676             case 'hbar':
27677                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27678                 break;
27679             case 'pie':
27680 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27681 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27682 //            
27683                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27684                 
27685                 break;
27686
27687         }
27688         
27689         if(this.title){
27690             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27691         }
27692         
27693     },
27694     
27695     setTitle: function(o)
27696     {
27697         this.title = o;
27698     },
27699     
27700     initEvents: function() {
27701         
27702         if(!this.href){
27703             this.el.on('click', this.onClick, this);
27704         }
27705     },
27706     
27707     onClick : function(e)
27708     {
27709         Roo.log('img onclick');
27710         this.fireEvent('click', this, e);
27711     }
27712    
27713 });
27714
27715  
27716 /*
27717  * - LGPL
27718  *
27719  * numberBox
27720  * 
27721  */
27722 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27723
27724 /**
27725  * @class Roo.bootstrap.dash.NumberBox
27726  * @extends Roo.bootstrap.Component
27727  * Bootstrap NumberBox class
27728  * @cfg {String} headline Box headline
27729  * @cfg {String} content Box content
27730  * @cfg {String} icon Box icon
27731  * @cfg {String} footer Footer text
27732  * @cfg {String} fhref Footer href
27733  * 
27734  * @constructor
27735  * Create a new NumberBox
27736  * @param {Object} config The config object
27737  */
27738
27739
27740 Roo.bootstrap.dash.NumberBox = function(config){
27741     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27742     
27743 };
27744
27745 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27746     
27747     headline : '',
27748     content : '',
27749     icon : '',
27750     footer : '',
27751     fhref : '',
27752     ficon : '',
27753     
27754     getAutoCreate : function(){
27755         
27756         var cfg = {
27757             tag : 'div',
27758             cls : 'small-box ',
27759             cn : [
27760                 {
27761                     tag : 'div',
27762                     cls : 'inner',
27763                     cn :[
27764                         {
27765                             tag : 'h3',
27766                             cls : 'roo-headline',
27767                             html : this.headline
27768                         },
27769                         {
27770                             tag : 'p',
27771                             cls : 'roo-content',
27772                             html : this.content
27773                         }
27774                     ]
27775                 }
27776             ]
27777         };
27778         
27779         if(this.icon){
27780             cfg.cn.push({
27781                 tag : 'div',
27782                 cls : 'icon',
27783                 cn :[
27784                     {
27785                         tag : 'i',
27786                         cls : 'ion ' + this.icon
27787                     }
27788                 ]
27789             });
27790         }
27791         
27792         if(this.footer){
27793             var footer = {
27794                 tag : 'a',
27795                 cls : 'small-box-footer',
27796                 href : this.fhref || '#',
27797                 html : this.footer
27798             };
27799             
27800             cfg.cn.push(footer);
27801             
27802         }
27803         
27804         return  cfg;
27805     },
27806
27807     onRender : function(ct,position){
27808         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27809
27810
27811        
27812                 
27813     },
27814
27815     setHeadline: function (value)
27816     {
27817         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27818     },
27819     
27820     setFooter: function (value, href)
27821     {
27822         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27823         
27824         if(href){
27825             this.el.select('a.small-box-footer',true).first().attr('href', href);
27826         }
27827         
27828     },
27829
27830     setContent: function (value)
27831     {
27832         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27833     },
27834
27835     initEvents: function() 
27836     {   
27837         
27838     }
27839     
27840 });
27841
27842  
27843 /*
27844  * - LGPL
27845  *
27846  * TabBox
27847  * 
27848  */
27849 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27850
27851 /**
27852  * @class Roo.bootstrap.dash.TabBox
27853  * @extends Roo.bootstrap.Component
27854  * Bootstrap TabBox class
27855  * @cfg {String} title Title of the TabBox
27856  * @cfg {String} icon Icon of the TabBox
27857  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27858  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27859  * 
27860  * @constructor
27861  * Create a new TabBox
27862  * @param {Object} config The config object
27863  */
27864
27865
27866 Roo.bootstrap.dash.TabBox = function(config){
27867     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27868     this.addEvents({
27869         // raw events
27870         /**
27871          * @event addpane
27872          * When a pane is added
27873          * @param {Roo.bootstrap.dash.TabPane} pane
27874          */
27875         "addpane" : true,
27876         /**
27877          * @event activatepane
27878          * When a pane is activated
27879          * @param {Roo.bootstrap.dash.TabPane} pane
27880          */
27881         "activatepane" : true
27882         
27883          
27884     });
27885     
27886     this.panes = [];
27887 };
27888
27889 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27890
27891     title : '',
27892     icon : false,
27893     showtabs : true,
27894     tabScrollable : false,
27895     
27896     getChildContainer : function()
27897     {
27898         return this.el.select('.tab-content', true).first();
27899     },
27900     
27901     getAutoCreate : function(){
27902         
27903         var header = {
27904             tag: 'li',
27905             cls: 'pull-left header',
27906             html: this.title,
27907             cn : []
27908         };
27909         
27910         if(this.icon){
27911             header.cn.push({
27912                 tag: 'i',
27913                 cls: 'fa ' + this.icon
27914             });
27915         }
27916         
27917         var h = {
27918             tag: 'ul',
27919             cls: 'nav nav-tabs pull-right',
27920             cn: [
27921                 header
27922             ]
27923         };
27924         
27925         if(this.tabScrollable){
27926             h = {
27927                 tag: 'div',
27928                 cls: 'tab-header',
27929                 cn: [
27930                     {
27931                         tag: 'ul',
27932                         cls: 'nav nav-tabs pull-right',
27933                         cn: [
27934                             header
27935                         ]
27936                     }
27937                 ]
27938             };
27939         }
27940         
27941         var cfg = {
27942             tag: 'div',
27943             cls: 'nav-tabs-custom',
27944             cn: [
27945                 h,
27946                 {
27947                     tag: 'div',
27948                     cls: 'tab-content no-padding',
27949                     cn: []
27950                 }
27951             ]
27952         };
27953
27954         return  cfg;
27955     },
27956     initEvents : function()
27957     {
27958         //Roo.log('add add pane handler');
27959         this.on('addpane', this.onAddPane, this);
27960     },
27961      /**
27962      * Updates the box title
27963      * @param {String} html to set the title to.
27964      */
27965     setTitle : function(value)
27966     {
27967         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27968     },
27969     onAddPane : function(pane)
27970     {
27971         this.panes.push(pane);
27972         //Roo.log('addpane');
27973         //Roo.log(pane);
27974         // tabs are rendere left to right..
27975         if(!this.showtabs){
27976             return;
27977         }
27978         
27979         var ctr = this.el.select('.nav-tabs', true).first();
27980          
27981          
27982         var existing = ctr.select('.nav-tab',true);
27983         var qty = existing.getCount();;
27984         
27985         
27986         var tab = ctr.createChild({
27987             tag : 'li',
27988             cls : 'nav-tab' + (qty ? '' : ' active'),
27989             cn : [
27990                 {
27991                     tag : 'a',
27992                     href:'#',
27993                     html : pane.title
27994                 }
27995             ]
27996         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27997         pane.tab = tab;
27998         
27999         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28000         if (!qty) {
28001             pane.el.addClass('active');
28002         }
28003         
28004                 
28005     },
28006     onTabClick : function(ev,un,ob,pane)
28007     {
28008         //Roo.log('tab - prev default');
28009         ev.preventDefault();
28010         
28011         
28012         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28013         pane.tab.addClass('active');
28014         //Roo.log(pane.title);
28015         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28016         // technically we should have a deactivate event.. but maybe add later.
28017         // and it should not de-activate the selected tab...
28018         this.fireEvent('activatepane', pane);
28019         pane.el.addClass('active');
28020         pane.fireEvent('activate');
28021         
28022         
28023     },
28024     
28025     getActivePane : function()
28026     {
28027         var r = false;
28028         Roo.each(this.panes, function(p) {
28029             if(p.el.hasClass('active')){
28030                 r = p;
28031                 return false;
28032             }
28033             
28034             return;
28035         });
28036         
28037         return r;
28038     }
28039     
28040     
28041 });
28042
28043  
28044 /*
28045  * - LGPL
28046  *
28047  * Tab pane
28048  * 
28049  */
28050 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28051 /**
28052  * @class Roo.bootstrap.TabPane
28053  * @extends Roo.bootstrap.Component
28054  * Bootstrap TabPane class
28055  * @cfg {Boolean} active (false | true) Default false
28056  * @cfg {String} title title of panel
28057
28058  * 
28059  * @constructor
28060  * Create a new TabPane
28061  * @param {Object} config The config object
28062  */
28063
28064 Roo.bootstrap.dash.TabPane = function(config){
28065     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28066     
28067     this.addEvents({
28068         // raw events
28069         /**
28070          * @event activate
28071          * When a pane is activated
28072          * @param {Roo.bootstrap.dash.TabPane} pane
28073          */
28074         "activate" : true
28075          
28076     });
28077 };
28078
28079 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28080     
28081     active : false,
28082     title : '',
28083     
28084     // the tabBox that this is attached to.
28085     tab : false,
28086      
28087     getAutoCreate : function() 
28088     {
28089         var cfg = {
28090             tag: 'div',
28091             cls: 'tab-pane'
28092         };
28093         
28094         if(this.active){
28095             cfg.cls += ' active';
28096         }
28097         
28098         return cfg;
28099     },
28100     initEvents  : function()
28101     {
28102         //Roo.log('trigger add pane handler');
28103         this.parent().fireEvent('addpane', this)
28104     },
28105     
28106      /**
28107      * Updates the tab title 
28108      * @param {String} html to set the title to.
28109      */
28110     setTitle: function(str)
28111     {
28112         if (!this.tab) {
28113             return;
28114         }
28115         this.title = str;
28116         this.tab.select('a', true).first().dom.innerHTML = str;
28117         
28118     }
28119     
28120     
28121     
28122 });
28123
28124  
28125
28126
28127  /*
28128  * - LGPL
28129  *
28130  * menu
28131  * 
28132  */
28133 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28134
28135 /**
28136  * @class Roo.bootstrap.menu.Menu
28137  * @extends Roo.bootstrap.Component
28138  * Bootstrap Menu class - container for Menu
28139  * @cfg {String} html Text of the menu
28140  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28141  * @cfg {String} icon Font awesome icon
28142  * @cfg {String} pos Menu align to (top | bottom) default bottom
28143  * 
28144  * 
28145  * @constructor
28146  * Create a new Menu
28147  * @param {Object} config The config object
28148  */
28149
28150
28151 Roo.bootstrap.menu.Menu = function(config){
28152     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28153     
28154     this.addEvents({
28155         /**
28156          * @event beforeshow
28157          * Fires before this menu is displayed
28158          * @param {Roo.bootstrap.menu.Menu} this
28159          */
28160         beforeshow : true,
28161         /**
28162          * @event beforehide
28163          * Fires before this menu is hidden
28164          * @param {Roo.bootstrap.menu.Menu} this
28165          */
28166         beforehide : true,
28167         /**
28168          * @event show
28169          * Fires after this menu is displayed
28170          * @param {Roo.bootstrap.menu.Menu} this
28171          */
28172         show : true,
28173         /**
28174          * @event hide
28175          * Fires after this menu is hidden
28176          * @param {Roo.bootstrap.menu.Menu} this
28177          */
28178         hide : true,
28179         /**
28180          * @event click
28181          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28182          * @param {Roo.bootstrap.menu.Menu} this
28183          * @param {Roo.EventObject} e
28184          */
28185         click : true
28186     });
28187     
28188 };
28189
28190 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28191     
28192     submenu : false,
28193     html : '',
28194     weight : 'default',
28195     icon : false,
28196     pos : 'bottom',
28197     
28198     
28199     getChildContainer : function() {
28200         if(this.isSubMenu){
28201             return this.el;
28202         }
28203         
28204         return this.el.select('ul.dropdown-menu', true).first();  
28205     },
28206     
28207     getAutoCreate : function()
28208     {
28209         var text = [
28210             {
28211                 tag : 'span',
28212                 cls : 'roo-menu-text',
28213                 html : this.html
28214             }
28215         ];
28216         
28217         if(this.icon){
28218             text.unshift({
28219                 tag : 'i',
28220                 cls : 'fa ' + this.icon
28221             })
28222         }
28223         
28224         
28225         var cfg = {
28226             tag : 'div',
28227             cls : 'btn-group',
28228             cn : [
28229                 {
28230                     tag : 'button',
28231                     cls : 'dropdown-button btn btn-' + this.weight,
28232                     cn : text
28233                 },
28234                 {
28235                     tag : 'button',
28236                     cls : 'dropdown-toggle btn btn-' + this.weight,
28237                     cn : [
28238                         {
28239                             tag : 'span',
28240                             cls : 'caret'
28241                         }
28242                     ]
28243                 },
28244                 {
28245                     tag : 'ul',
28246                     cls : 'dropdown-menu'
28247                 }
28248             ]
28249             
28250         };
28251         
28252         if(this.pos == 'top'){
28253             cfg.cls += ' dropup';
28254         }
28255         
28256         if(this.isSubMenu){
28257             cfg = {
28258                 tag : 'ul',
28259                 cls : 'dropdown-menu'
28260             }
28261         }
28262         
28263         return cfg;
28264     },
28265     
28266     onRender : function(ct, position)
28267     {
28268         this.isSubMenu = ct.hasClass('dropdown-submenu');
28269         
28270         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28271     },
28272     
28273     initEvents : function() 
28274     {
28275         if(this.isSubMenu){
28276             return;
28277         }
28278         
28279         this.hidden = true;
28280         
28281         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28282         this.triggerEl.on('click', this.onTriggerPress, this);
28283         
28284         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28285         this.buttonEl.on('click', this.onClick, this);
28286         
28287     },
28288     
28289     list : function()
28290     {
28291         if(this.isSubMenu){
28292             return this.el;
28293         }
28294         
28295         return this.el.select('ul.dropdown-menu', true).first();
28296     },
28297     
28298     onClick : function(e)
28299     {
28300         this.fireEvent("click", this, e);
28301     },
28302     
28303     onTriggerPress  : function(e)
28304     {   
28305         if (this.isVisible()) {
28306             this.hide();
28307         } else {
28308             this.show();
28309         }
28310     },
28311     
28312     isVisible : function(){
28313         return !this.hidden;
28314     },
28315     
28316     show : function()
28317     {
28318         this.fireEvent("beforeshow", this);
28319         
28320         this.hidden = false;
28321         this.el.addClass('open');
28322         
28323         Roo.get(document).on("mouseup", this.onMouseUp, this);
28324         
28325         this.fireEvent("show", this);
28326         
28327         
28328     },
28329     
28330     hide : function()
28331     {
28332         this.fireEvent("beforehide", this);
28333         
28334         this.hidden = true;
28335         this.el.removeClass('open');
28336         
28337         Roo.get(document).un("mouseup", this.onMouseUp);
28338         
28339         this.fireEvent("hide", this);
28340     },
28341     
28342     onMouseUp : function()
28343     {
28344         this.hide();
28345     }
28346     
28347 });
28348
28349  
28350  /*
28351  * - LGPL
28352  *
28353  * menu item
28354  * 
28355  */
28356 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28357
28358 /**
28359  * @class Roo.bootstrap.menu.Item
28360  * @extends Roo.bootstrap.Component
28361  * Bootstrap MenuItem class
28362  * @cfg {Boolean} submenu (true | false) default false
28363  * @cfg {String} html text of the item
28364  * @cfg {String} href the link
28365  * @cfg {Boolean} disable (true | false) default false
28366  * @cfg {Boolean} preventDefault (true | false) default true
28367  * @cfg {String} icon Font awesome icon
28368  * @cfg {String} pos Submenu align to (left | right) default right 
28369  * 
28370  * 
28371  * @constructor
28372  * Create a new Item
28373  * @param {Object} config The config object
28374  */
28375
28376
28377 Roo.bootstrap.menu.Item = function(config){
28378     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28379     this.addEvents({
28380         /**
28381          * @event mouseover
28382          * Fires when the mouse is hovering over this menu
28383          * @param {Roo.bootstrap.menu.Item} this
28384          * @param {Roo.EventObject} e
28385          */
28386         mouseover : true,
28387         /**
28388          * @event mouseout
28389          * Fires when the mouse exits this menu
28390          * @param {Roo.bootstrap.menu.Item} this
28391          * @param {Roo.EventObject} e
28392          */
28393         mouseout : true,
28394         // raw events
28395         /**
28396          * @event click
28397          * The raw click event for the entire grid.
28398          * @param {Roo.EventObject} e
28399          */
28400         click : true
28401     });
28402 };
28403
28404 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28405     
28406     submenu : false,
28407     href : '',
28408     html : '',
28409     preventDefault: true,
28410     disable : false,
28411     icon : false,
28412     pos : 'right',
28413     
28414     getAutoCreate : function()
28415     {
28416         var text = [
28417             {
28418                 tag : 'span',
28419                 cls : 'roo-menu-item-text',
28420                 html : this.html
28421             }
28422         ];
28423         
28424         if(this.icon){
28425             text.unshift({
28426                 tag : 'i',
28427                 cls : 'fa ' + this.icon
28428             })
28429         }
28430         
28431         var cfg = {
28432             tag : 'li',
28433             cn : [
28434                 {
28435                     tag : 'a',
28436                     href : this.href || '#',
28437                     cn : text
28438                 }
28439             ]
28440         };
28441         
28442         if(this.disable){
28443             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28444         }
28445         
28446         if(this.submenu){
28447             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28448             
28449             if(this.pos == 'left'){
28450                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28451             }
28452         }
28453         
28454         return cfg;
28455     },
28456     
28457     initEvents : function() 
28458     {
28459         this.el.on('mouseover', this.onMouseOver, this);
28460         this.el.on('mouseout', this.onMouseOut, this);
28461         
28462         this.el.select('a', true).first().on('click', this.onClick, this);
28463         
28464     },
28465     
28466     onClick : function(e)
28467     {
28468         if(this.preventDefault){
28469             e.preventDefault();
28470         }
28471         
28472         this.fireEvent("click", this, e);
28473     },
28474     
28475     onMouseOver : function(e)
28476     {
28477         if(this.submenu && this.pos == 'left'){
28478             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28479         }
28480         
28481         this.fireEvent("mouseover", this, e);
28482     },
28483     
28484     onMouseOut : function(e)
28485     {
28486         this.fireEvent("mouseout", this, e);
28487     }
28488 });
28489
28490  
28491
28492  /*
28493  * - LGPL
28494  *
28495  * menu separator
28496  * 
28497  */
28498 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28499
28500 /**
28501  * @class Roo.bootstrap.menu.Separator
28502  * @extends Roo.bootstrap.Component
28503  * Bootstrap Separator class
28504  * 
28505  * @constructor
28506  * Create a new Separator
28507  * @param {Object} config The config object
28508  */
28509
28510
28511 Roo.bootstrap.menu.Separator = function(config){
28512     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28513 };
28514
28515 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28516     
28517     getAutoCreate : function(){
28518         var cfg = {
28519             tag : 'li',
28520             cls: 'divider'
28521         };
28522         
28523         return cfg;
28524     }
28525    
28526 });
28527
28528  
28529
28530  /*
28531  * - LGPL
28532  *
28533  * Tooltip
28534  * 
28535  */
28536
28537 /**
28538  * @class Roo.bootstrap.Tooltip
28539  * Bootstrap Tooltip class
28540  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28541  * to determine which dom element triggers the tooltip.
28542  * 
28543  * It needs to add support for additional attributes like tooltip-position
28544  * 
28545  * @constructor
28546  * Create a new Toolti
28547  * @param {Object} config The config object
28548  */
28549
28550 Roo.bootstrap.Tooltip = function(config){
28551     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28552     
28553     this.alignment = Roo.bootstrap.Tooltip.alignment;
28554     
28555     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28556         this.alignment = config.alignment;
28557     }
28558     
28559 };
28560
28561 Roo.apply(Roo.bootstrap.Tooltip, {
28562     /**
28563      * @function init initialize tooltip monitoring.
28564      * @static
28565      */
28566     currentEl : false,
28567     currentTip : false,
28568     currentRegion : false,
28569     
28570     //  init : delay?
28571     
28572     init : function()
28573     {
28574         Roo.get(document).on('mouseover', this.enter ,this);
28575         Roo.get(document).on('mouseout', this.leave, this);
28576          
28577         
28578         this.currentTip = new Roo.bootstrap.Tooltip();
28579     },
28580     
28581     enter : function(ev)
28582     {
28583         var dom = ev.getTarget();
28584         
28585         //Roo.log(['enter',dom]);
28586         var el = Roo.fly(dom);
28587         if (this.currentEl) {
28588             //Roo.log(dom);
28589             //Roo.log(this.currentEl);
28590             //Roo.log(this.currentEl.contains(dom));
28591             if (this.currentEl == el) {
28592                 return;
28593             }
28594             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28595                 return;
28596             }
28597
28598         }
28599         
28600         if (this.currentTip.el) {
28601             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28602         }    
28603         //Roo.log(ev);
28604         
28605         if(!el || el.dom == document){
28606             return;
28607         }
28608         
28609         var bindEl = el;
28610         
28611         // you can not look for children, as if el is the body.. then everythign is the child..
28612         if (!el.attr('tooltip')) { //
28613             if (!el.select("[tooltip]").elements.length) {
28614                 return;
28615             }
28616             // is the mouse over this child...?
28617             bindEl = el.select("[tooltip]").first();
28618             var xy = ev.getXY();
28619             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28620                 //Roo.log("not in region.");
28621                 return;
28622             }
28623             //Roo.log("child element over..");
28624             
28625         }
28626         this.currentEl = bindEl;
28627         this.currentTip.bind(bindEl);
28628         this.currentRegion = Roo.lib.Region.getRegion(dom);
28629         this.currentTip.enter();
28630         
28631     },
28632     leave : function(ev)
28633     {
28634         var dom = ev.getTarget();
28635         //Roo.log(['leave',dom]);
28636         if (!this.currentEl) {
28637             return;
28638         }
28639         
28640         
28641         if (dom != this.currentEl.dom) {
28642             return;
28643         }
28644         var xy = ev.getXY();
28645         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28646             return;
28647         }
28648         // only activate leave if mouse cursor is outside... bounding box..
28649         
28650         
28651         
28652         
28653         if (this.currentTip) {
28654             this.currentTip.leave();
28655         }
28656         //Roo.log('clear currentEl');
28657         this.currentEl = false;
28658         
28659         
28660     },
28661     alignment : {
28662         'left' : ['r-l', [-2,0], 'right'],
28663         'right' : ['l-r', [2,0], 'left'],
28664         'bottom' : ['t-b', [0,2], 'top'],
28665         'top' : [ 'b-t', [0,-2], 'bottom']
28666     }
28667     
28668 });
28669
28670
28671 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28672     
28673     
28674     bindEl : false,
28675     
28676     delay : null, // can be { show : 300 , hide: 500}
28677     
28678     timeout : null,
28679     
28680     hoverState : null, //???
28681     
28682     placement : 'bottom', 
28683     
28684     alignment : false,
28685     
28686     getAutoCreate : function(){
28687     
28688         var cfg = {
28689            cls : 'tooltip',   
28690            role : 'tooltip',
28691            cn : [
28692                 {
28693                     cls : 'tooltip-arrow arrow'
28694                 },
28695                 {
28696                     cls : 'tooltip-inner'
28697                 }
28698            ]
28699         };
28700         
28701         return cfg;
28702     },
28703     bind : function(el)
28704     {
28705         this.bindEl = el;
28706     },
28707     
28708     initEvents : function()
28709     {
28710         this.arrowEl = this.el.select('.arrow', true).first();
28711         this.innerEl = this.el.select('.tooltip-inner', true).first();
28712     },
28713     
28714     enter : function () {
28715        
28716         if (this.timeout != null) {
28717             clearTimeout(this.timeout);
28718         }
28719         
28720         this.hoverState = 'in';
28721          //Roo.log("enter - show");
28722         if (!this.delay || !this.delay.show) {
28723             this.show();
28724             return;
28725         }
28726         var _t = this;
28727         this.timeout = setTimeout(function () {
28728             if (_t.hoverState == 'in') {
28729                 _t.show();
28730             }
28731         }, this.delay.show);
28732     },
28733     leave : function()
28734     {
28735         clearTimeout(this.timeout);
28736     
28737         this.hoverState = 'out';
28738          if (!this.delay || !this.delay.hide) {
28739             this.hide();
28740             return;
28741         }
28742        
28743         var _t = this;
28744         this.timeout = setTimeout(function () {
28745             //Roo.log("leave - timeout");
28746             
28747             if (_t.hoverState == 'out') {
28748                 _t.hide();
28749                 Roo.bootstrap.Tooltip.currentEl = false;
28750             }
28751         }, delay);
28752     },
28753     
28754     show : function (msg)
28755     {
28756         if (!this.el) {
28757             this.render(document.body);
28758         }
28759         // set content.
28760         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28761         
28762         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28763         
28764         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28765         
28766         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28767                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28768         
28769         var placement = typeof this.placement == 'function' ?
28770             this.placement.call(this, this.el, on_el) :
28771             this.placement;
28772             
28773         var autoToken = /\s?auto?\s?/i;
28774         var autoPlace = autoToken.test(placement);
28775         if (autoPlace) {
28776             placement = placement.replace(autoToken, '') || 'top';
28777         }
28778         
28779         //this.el.detach()
28780         //this.el.setXY([0,0]);
28781         this.el.show();
28782         //this.el.dom.style.display='block';
28783         
28784         //this.el.appendTo(on_el);
28785         
28786         var p = this.getPosition();
28787         var box = this.el.getBox();
28788         
28789         if (autoPlace) {
28790             // fixme..
28791         }
28792         
28793         var align = this.alignment[placement];
28794         
28795         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28796         
28797         if(placement == 'top' || placement == 'bottom'){
28798             if(xy[0] < 0){
28799                 placement = 'right';
28800             }
28801             
28802             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28803                 placement = 'left';
28804             }
28805             
28806             var scroll = Roo.select('body', true).first().getScroll();
28807             
28808             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28809                 placement = 'top';
28810             }
28811             
28812             align = this.alignment[placement];
28813             
28814             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28815             
28816         }
28817         
28818         this.el.alignTo(this.bindEl, align[0],align[1]);
28819         //var arrow = this.el.select('.arrow',true).first();
28820         //arrow.set(align[2], 
28821         
28822         this.el.addClass(placement);
28823         this.el.addClass("bs-tooltip-"+ placement);
28824         
28825         this.el.addClass('in fade show');
28826         
28827         this.hoverState = null;
28828         
28829         if (this.el.hasClass('fade')) {
28830             // fade it?
28831         }
28832         
28833         
28834         
28835         
28836         
28837     },
28838     hide : function()
28839     {
28840          
28841         if (!this.el) {
28842             return;
28843         }
28844         //this.el.setXY([0,0]);
28845         this.el.removeClass(['show', 'in']);
28846         //this.el.hide();
28847         
28848     }
28849     
28850 });
28851  
28852
28853  /*
28854  * - LGPL
28855  *
28856  * Location Picker
28857  * 
28858  */
28859
28860 /**
28861  * @class Roo.bootstrap.LocationPicker
28862  * @extends Roo.bootstrap.Component
28863  * Bootstrap LocationPicker class
28864  * @cfg {Number} latitude Position when init default 0
28865  * @cfg {Number} longitude Position when init default 0
28866  * @cfg {Number} zoom default 15
28867  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28868  * @cfg {Boolean} mapTypeControl default false
28869  * @cfg {Boolean} disableDoubleClickZoom default false
28870  * @cfg {Boolean} scrollwheel default true
28871  * @cfg {Boolean} streetViewControl default false
28872  * @cfg {Number} radius default 0
28873  * @cfg {String} locationName
28874  * @cfg {Boolean} draggable default true
28875  * @cfg {Boolean} enableAutocomplete default false
28876  * @cfg {Boolean} enableReverseGeocode default true
28877  * @cfg {String} markerTitle
28878  * 
28879  * @constructor
28880  * Create a new LocationPicker
28881  * @param {Object} config The config object
28882  */
28883
28884
28885 Roo.bootstrap.LocationPicker = function(config){
28886     
28887     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28888     
28889     this.addEvents({
28890         /**
28891          * @event initial
28892          * Fires when the picker initialized.
28893          * @param {Roo.bootstrap.LocationPicker} this
28894          * @param {Google Location} location
28895          */
28896         initial : true,
28897         /**
28898          * @event positionchanged
28899          * Fires when the picker position changed.
28900          * @param {Roo.bootstrap.LocationPicker} this
28901          * @param {Google Location} location
28902          */
28903         positionchanged : true,
28904         /**
28905          * @event resize
28906          * Fires when the map resize.
28907          * @param {Roo.bootstrap.LocationPicker} this
28908          */
28909         resize : true,
28910         /**
28911          * @event show
28912          * Fires when the map show.
28913          * @param {Roo.bootstrap.LocationPicker} this
28914          */
28915         show : true,
28916         /**
28917          * @event hide
28918          * Fires when the map hide.
28919          * @param {Roo.bootstrap.LocationPicker} this
28920          */
28921         hide : true,
28922         /**
28923          * @event mapClick
28924          * Fires when click the map.
28925          * @param {Roo.bootstrap.LocationPicker} this
28926          * @param {Map event} e
28927          */
28928         mapClick : true,
28929         /**
28930          * @event mapRightClick
28931          * Fires when right click the map.
28932          * @param {Roo.bootstrap.LocationPicker} this
28933          * @param {Map event} e
28934          */
28935         mapRightClick : true,
28936         /**
28937          * @event markerClick
28938          * Fires when click the marker.
28939          * @param {Roo.bootstrap.LocationPicker} this
28940          * @param {Map event} e
28941          */
28942         markerClick : true,
28943         /**
28944          * @event markerRightClick
28945          * Fires when right click the marker.
28946          * @param {Roo.bootstrap.LocationPicker} this
28947          * @param {Map event} e
28948          */
28949         markerRightClick : true,
28950         /**
28951          * @event OverlayViewDraw
28952          * Fires when OverlayView Draw
28953          * @param {Roo.bootstrap.LocationPicker} this
28954          */
28955         OverlayViewDraw : true,
28956         /**
28957          * @event OverlayViewOnAdd
28958          * Fires when OverlayView Draw
28959          * @param {Roo.bootstrap.LocationPicker} this
28960          */
28961         OverlayViewOnAdd : true,
28962         /**
28963          * @event OverlayViewOnRemove
28964          * Fires when OverlayView Draw
28965          * @param {Roo.bootstrap.LocationPicker} this
28966          */
28967         OverlayViewOnRemove : true,
28968         /**
28969          * @event OverlayViewShow
28970          * Fires when OverlayView Draw
28971          * @param {Roo.bootstrap.LocationPicker} this
28972          * @param {Pixel} cpx
28973          */
28974         OverlayViewShow : true,
28975         /**
28976          * @event OverlayViewHide
28977          * Fires when OverlayView Draw
28978          * @param {Roo.bootstrap.LocationPicker} this
28979          */
28980         OverlayViewHide : true,
28981         /**
28982          * @event loadexception
28983          * Fires when load google lib failed.
28984          * @param {Roo.bootstrap.LocationPicker} this
28985          */
28986         loadexception : true
28987     });
28988         
28989 };
28990
28991 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28992     
28993     gMapContext: false,
28994     
28995     latitude: 0,
28996     longitude: 0,
28997     zoom: 15,
28998     mapTypeId: false,
28999     mapTypeControl: false,
29000     disableDoubleClickZoom: false,
29001     scrollwheel: true,
29002     streetViewControl: false,
29003     radius: 0,
29004     locationName: '',
29005     draggable: true,
29006     enableAutocomplete: false,
29007     enableReverseGeocode: true,
29008     markerTitle: '',
29009     
29010     getAutoCreate: function()
29011     {
29012
29013         var cfg = {
29014             tag: 'div',
29015             cls: 'roo-location-picker'
29016         };
29017         
29018         return cfg
29019     },
29020     
29021     initEvents: function(ct, position)
29022     {       
29023         if(!this.el.getWidth() || this.isApplied()){
29024             return;
29025         }
29026         
29027         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29028         
29029         this.initial();
29030     },
29031     
29032     initial: function()
29033     {
29034         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29035             this.fireEvent('loadexception', this);
29036             return;
29037         }
29038         
29039         if(!this.mapTypeId){
29040             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29041         }
29042         
29043         this.gMapContext = this.GMapContext();
29044         
29045         this.initOverlayView();
29046         
29047         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29048         
29049         var _this = this;
29050                 
29051         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29052             _this.setPosition(_this.gMapContext.marker.position);
29053         });
29054         
29055         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29056             _this.fireEvent('mapClick', this, event);
29057             
29058         });
29059
29060         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29061             _this.fireEvent('mapRightClick', this, event);
29062             
29063         });
29064         
29065         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29066             _this.fireEvent('markerClick', this, event);
29067             
29068         });
29069
29070         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29071             _this.fireEvent('markerRightClick', this, event);
29072             
29073         });
29074         
29075         this.setPosition(this.gMapContext.location);
29076         
29077         this.fireEvent('initial', this, this.gMapContext.location);
29078     },
29079     
29080     initOverlayView: function()
29081     {
29082         var _this = this;
29083         
29084         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29085             
29086             draw: function()
29087             {
29088                 _this.fireEvent('OverlayViewDraw', _this);
29089             },
29090             
29091             onAdd: function()
29092             {
29093                 _this.fireEvent('OverlayViewOnAdd', _this);
29094             },
29095             
29096             onRemove: function()
29097             {
29098                 _this.fireEvent('OverlayViewOnRemove', _this);
29099             },
29100             
29101             show: function(cpx)
29102             {
29103                 _this.fireEvent('OverlayViewShow', _this, cpx);
29104             },
29105             
29106             hide: function()
29107             {
29108                 _this.fireEvent('OverlayViewHide', _this);
29109             }
29110             
29111         });
29112     },
29113     
29114     fromLatLngToContainerPixel: function(event)
29115     {
29116         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29117     },
29118     
29119     isApplied: function() 
29120     {
29121         return this.getGmapContext() == false ? false : true;
29122     },
29123     
29124     getGmapContext: function() 
29125     {
29126         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29127     },
29128     
29129     GMapContext: function() 
29130     {
29131         var position = new google.maps.LatLng(this.latitude, this.longitude);
29132         
29133         var _map = new google.maps.Map(this.el.dom, {
29134             center: position,
29135             zoom: this.zoom,
29136             mapTypeId: this.mapTypeId,
29137             mapTypeControl: this.mapTypeControl,
29138             disableDoubleClickZoom: this.disableDoubleClickZoom,
29139             scrollwheel: this.scrollwheel,
29140             streetViewControl: this.streetViewControl,
29141             locationName: this.locationName,
29142             draggable: this.draggable,
29143             enableAutocomplete: this.enableAutocomplete,
29144             enableReverseGeocode: this.enableReverseGeocode
29145         });
29146         
29147         var _marker = new google.maps.Marker({
29148             position: position,
29149             map: _map,
29150             title: this.markerTitle,
29151             draggable: this.draggable
29152         });
29153         
29154         return {
29155             map: _map,
29156             marker: _marker,
29157             circle: null,
29158             location: position,
29159             radius: this.radius,
29160             locationName: this.locationName,
29161             addressComponents: {
29162                 formatted_address: null,
29163                 addressLine1: null,
29164                 addressLine2: null,
29165                 streetName: null,
29166                 streetNumber: null,
29167                 city: null,
29168                 district: null,
29169                 state: null,
29170                 stateOrProvince: null
29171             },
29172             settings: this,
29173             domContainer: this.el.dom,
29174             geodecoder: new google.maps.Geocoder()
29175         };
29176     },
29177     
29178     drawCircle: function(center, radius, options) 
29179     {
29180         if (this.gMapContext.circle != null) {
29181             this.gMapContext.circle.setMap(null);
29182         }
29183         if (radius > 0) {
29184             radius *= 1;
29185             options = Roo.apply({}, options, {
29186                 strokeColor: "#0000FF",
29187                 strokeOpacity: .35,
29188                 strokeWeight: 2,
29189                 fillColor: "#0000FF",
29190                 fillOpacity: .2
29191             });
29192             
29193             options.map = this.gMapContext.map;
29194             options.radius = radius;
29195             options.center = center;
29196             this.gMapContext.circle = new google.maps.Circle(options);
29197             return this.gMapContext.circle;
29198         }
29199         
29200         return null;
29201     },
29202     
29203     setPosition: function(location) 
29204     {
29205         this.gMapContext.location = location;
29206         this.gMapContext.marker.setPosition(location);
29207         this.gMapContext.map.panTo(location);
29208         this.drawCircle(location, this.gMapContext.radius, {});
29209         
29210         var _this = this;
29211         
29212         if (this.gMapContext.settings.enableReverseGeocode) {
29213             this.gMapContext.geodecoder.geocode({
29214                 latLng: this.gMapContext.location
29215             }, function(results, status) {
29216                 
29217                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29218                     _this.gMapContext.locationName = results[0].formatted_address;
29219                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29220                     
29221                     _this.fireEvent('positionchanged', this, location);
29222                 }
29223             });
29224             
29225             return;
29226         }
29227         
29228         this.fireEvent('positionchanged', this, location);
29229     },
29230     
29231     resize: function()
29232     {
29233         google.maps.event.trigger(this.gMapContext.map, "resize");
29234         
29235         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29236         
29237         this.fireEvent('resize', this);
29238     },
29239     
29240     setPositionByLatLng: function(latitude, longitude)
29241     {
29242         this.setPosition(new google.maps.LatLng(latitude, longitude));
29243     },
29244     
29245     getCurrentPosition: function() 
29246     {
29247         return {
29248             latitude: this.gMapContext.location.lat(),
29249             longitude: this.gMapContext.location.lng()
29250         };
29251     },
29252     
29253     getAddressName: function() 
29254     {
29255         return this.gMapContext.locationName;
29256     },
29257     
29258     getAddressComponents: function() 
29259     {
29260         return this.gMapContext.addressComponents;
29261     },
29262     
29263     address_component_from_google_geocode: function(address_components) 
29264     {
29265         var result = {};
29266         
29267         for (var i = 0; i < address_components.length; i++) {
29268             var component = address_components[i];
29269             if (component.types.indexOf("postal_code") >= 0) {
29270                 result.postalCode = component.short_name;
29271             } else if (component.types.indexOf("street_number") >= 0) {
29272                 result.streetNumber = component.short_name;
29273             } else if (component.types.indexOf("route") >= 0) {
29274                 result.streetName = component.short_name;
29275             } else if (component.types.indexOf("neighborhood") >= 0) {
29276                 result.city = component.short_name;
29277             } else if (component.types.indexOf("locality") >= 0) {
29278                 result.city = component.short_name;
29279             } else if (component.types.indexOf("sublocality") >= 0) {
29280                 result.district = component.short_name;
29281             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29282                 result.stateOrProvince = component.short_name;
29283             } else if (component.types.indexOf("country") >= 0) {
29284                 result.country = component.short_name;
29285             }
29286         }
29287         
29288         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29289         result.addressLine2 = "";
29290         return result;
29291     },
29292     
29293     setZoomLevel: function(zoom)
29294     {
29295         this.gMapContext.map.setZoom(zoom);
29296     },
29297     
29298     show: function()
29299     {
29300         if(!this.el){
29301             return;
29302         }
29303         
29304         this.el.show();
29305         
29306         this.resize();
29307         
29308         this.fireEvent('show', this);
29309     },
29310     
29311     hide: function()
29312     {
29313         if(!this.el){
29314             return;
29315         }
29316         
29317         this.el.hide();
29318         
29319         this.fireEvent('hide', this);
29320     }
29321     
29322 });
29323
29324 Roo.apply(Roo.bootstrap.LocationPicker, {
29325     
29326     OverlayView : function(map, options)
29327     {
29328         options = options || {};
29329         
29330         this.setMap(map);
29331     }
29332     
29333     
29334 });/**
29335  * @class Roo.bootstrap.Alert
29336  * @extends Roo.bootstrap.Component
29337  * Bootstrap Alert class - shows an alert area box
29338  * eg
29339  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29340   Enter a valid email address
29341 </div>
29342  * @licence LGPL
29343  * @cfg {String} title The title of alert
29344  * @cfg {String} html The content of alert
29345  * @cfg {String} weight (  success | info | warning | danger )
29346  * @cfg {String} faicon font-awesomeicon
29347  * 
29348  * @constructor
29349  * Create a new alert
29350  * @param {Object} config The config object
29351  */
29352
29353
29354 Roo.bootstrap.Alert = function(config){
29355     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29356     
29357 };
29358
29359 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29360     
29361     title: '',
29362     html: '',
29363     weight: false,
29364     faicon: false,
29365     
29366     getAutoCreate : function()
29367     {
29368         
29369         var cfg = {
29370             tag : 'div',
29371             cls : 'alert',
29372             cn : [
29373                 {
29374                     tag : 'i',
29375                     cls : 'roo-alert-icon'
29376                     
29377                 },
29378                 {
29379                     tag : 'b',
29380                     cls : 'roo-alert-title',
29381                     html : this.title
29382                 },
29383                 {
29384                     tag : 'span',
29385                     cls : 'roo-alert-text',
29386                     html : this.html
29387                 }
29388             ]
29389         };
29390         
29391         if(this.faicon){
29392             cfg.cn[0].cls += ' fa ' + this.faicon;
29393         }
29394         
29395         if(this.weight){
29396             cfg.cls += ' alert-' + this.weight;
29397         }
29398         
29399         return cfg;
29400     },
29401     
29402     initEvents: function() 
29403     {
29404         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29405     },
29406     
29407     setTitle : function(str)
29408     {
29409         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29410     },
29411     
29412     setText : function(str)
29413     {
29414         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29415     },
29416     
29417     setWeight : function(weight)
29418     {
29419         if(this.weight){
29420             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29421         }
29422         
29423         this.weight = weight;
29424         
29425         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29426     },
29427     
29428     setIcon : function(icon)
29429     {
29430         if(this.faicon){
29431             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29432         }
29433         
29434         this.faicon = icon;
29435         
29436         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29437     },
29438     
29439     hide: function() 
29440     {
29441         this.el.hide();   
29442     },
29443     
29444     show: function() 
29445     {  
29446         this.el.show();   
29447     }
29448     
29449 });
29450
29451  
29452 /*
29453 * Licence: LGPL
29454 */
29455
29456 /**
29457  * @class Roo.bootstrap.UploadCropbox
29458  * @extends Roo.bootstrap.Component
29459  * Bootstrap UploadCropbox class
29460  * @cfg {String} emptyText show when image has been loaded
29461  * @cfg {String} rotateNotify show when image too small to rotate
29462  * @cfg {Number} errorTimeout default 3000
29463  * @cfg {Number} minWidth default 300
29464  * @cfg {Number} minHeight default 300
29465  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29466  * @cfg {Boolean} isDocument (true|false) default false
29467  * @cfg {String} url action url
29468  * @cfg {String} paramName default 'imageUpload'
29469  * @cfg {String} method default POST
29470  * @cfg {Boolean} loadMask (true|false) default true
29471  * @cfg {Boolean} loadingText default 'Loading...'
29472  * 
29473  * @constructor
29474  * Create a new UploadCropbox
29475  * @param {Object} config The config object
29476  */
29477
29478 Roo.bootstrap.UploadCropbox = function(config){
29479     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29480     
29481     this.addEvents({
29482         /**
29483          * @event beforeselectfile
29484          * Fire before select file
29485          * @param {Roo.bootstrap.UploadCropbox} this
29486          */
29487         "beforeselectfile" : true,
29488         /**
29489          * @event initial
29490          * Fire after initEvent
29491          * @param {Roo.bootstrap.UploadCropbox} this
29492          */
29493         "initial" : true,
29494         /**
29495          * @event crop
29496          * Fire after initEvent
29497          * @param {Roo.bootstrap.UploadCropbox} this
29498          * @param {String} data
29499          */
29500         "crop" : true,
29501         /**
29502          * @event prepare
29503          * Fire when preparing the file data
29504          * @param {Roo.bootstrap.UploadCropbox} this
29505          * @param {Object} file
29506          */
29507         "prepare" : true,
29508         /**
29509          * @event exception
29510          * Fire when get exception
29511          * @param {Roo.bootstrap.UploadCropbox} this
29512          * @param {XMLHttpRequest} xhr
29513          */
29514         "exception" : true,
29515         /**
29516          * @event beforeloadcanvas
29517          * Fire before load the canvas
29518          * @param {Roo.bootstrap.UploadCropbox} this
29519          * @param {String} src
29520          */
29521         "beforeloadcanvas" : true,
29522         /**
29523          * @event trash
29524          * Fire when trash image
29525          * @param {Roo.bootstrap.UploadCropbox} this
29526          */
29527         "trash" : true,
29528         /**
29529          * @event download
29530          * Fire when download the image
29531          * @param {Roo.bootstrap.UploadCropbox} this
29532          */
29533         "download" : true,
29534         /**
29535          * @event footerbuttonclick
29536          * Fire when footerbuttonclick
29537          * @param {Roo.bootstrap.UploadCropbox} this
29538          * @param {String} type
29539          */
29540         "footerbuttonclick" : true,
29541         /**
29542          * @event resize
29543          * Fire when resize
29544          * @param {Roo.bootstrap.UploadCropbox} this
29545          */
29546         "resize" : true,
29547         /**
29548          * @event rotate
29549          * Fire when rotate the image
29550          * @param {Roo.bootstrap.UploadCropbox} this
29551          * @param {String} pos
29552          */
29553         "rotate" : true,
29554         /**
29555          * @event inspect
29556          * Fire when inspect the file
29557          * @param {Roo.bootstrap.UploadCropbox} this
29558          * @param {Object} file
29559          */
29560         "inspect" : true,
29561         /**
29562          * @event upload
29563          * Fire when xhr upload the file
29564          * @param {Roo.bootstrap.UploadCropbox} this
29565          * @param {Object} data
29566          */
29567         "upload" : true,
29568         /**
29569          * @event arrange
29570          * Fire when arrange the file data
29571          * @param {Roo.bootstrap.UploadCropbox} this
29572          * @param {Object} formData
29573          */
29574         "arrange" : true
29575     });
29576     
29577     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29578 };
29579
29580 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29581     
29582     emptyText : 'Click to upload image',
29583     rotateNotify : 'Image is too small to rotate',
29584     errorTimeout : 3000,
29585     scale : 0,
29586     baseScale : 1,
29587     rotate : 0,
29588     dragable : false,
29589     pinching : false,
29590     mouseX : 0,
29591     mouseY : 0,
29592     cropData : false,
29593     minWidth : 300,
29594     minHeight : 300,
29595     file : false,
29596     exif : {},
29597     baseRotate : 1,
29598     cropType : 'image/jpeg',
29599     buttons : false,
29600     canvasLoaded : false,
29601     isDocument : false,
29602     method : 'POST',
29603     paramName : 'imageUpload',
29604     loadMask : true,
29605     loadingText : 'Loading...',
29606     maskEl : false,
29607     
29608     getAutoCreate : function()
29609     {
29610         var cfg = {
29611             tag : 'div',
29612             cls : 'roo-upload-cropbox',
29613             cn : [
29614                 {
29615                     tag : 'input',
29616                     cls : 'roo-upload-cropbox-selector',
29617                     type : 'file'
29618                 },
29619                 {
29620                     tag : 'div',
29621                     cls : 'roo-upload-cropbox-body',
29622                     style : 'cursor:pointer',
29623                     cn : [
29624                         {
29625                             tag : 'div',
29626                             cls : 'roo-upload-cropbox-preview'
29627                         },
29628                         {
29629                             tag : 'div',
29630                             cls : 'roo-upload-cropbox-thumb'
29631                         },
29632                         {
29633                             tag : 'div',
29634                             cls : 'roo-upload-cropbox-empty-notify',
29635                             html : this.emptyText
29636                         },
29637                         {
29638                             tag : 'div',
29639                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29640                             html : this.rotateNotify
29641                         }
29642                     ]
29643                 },
29644                 {
29645                     tag : 'div',
29646                     cls : 'roo-upload-cropbox-footer',
29647                     cn : {
29648                         tag : 'div',
29649                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29650                         cn : []
29651                     }
29652                 }
29653             ]
29654         };
29655         
29656         return cfg;
29657     },
29658     
29659     onRender : function(ct, position)
29660     {
29661         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29662         
29663         if (this.buttons.length) {
29664             
29665             Roo.each(this.buttons, function(bb) {
29666                 
29667                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29668                 
29669                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29670                 
29671             }, this);
29672         }
29673         
29674         if(this.loadMask){
29675             this.maskEl = this.el;
29676         }
29677     },
29678     
29679     initEvents : function()
29680     {
29681         this.urlAPI = (window.createObjectURL && window) || 
29682                                 (window.URL && URL.revokeObjectURL && URL) || 
29683                                 (window.webkitURL && webkitURL);
29684                         
29685         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29686         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29687         
29688         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29689         this.selectorEl.hide();
29690         
29691         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29692         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29693         
29694         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29695         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29696         this.thumbEl.hide();
29697         
29698         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29699         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29700         
29701         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29702         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29703         this.errorEl.hide();
29704         
29705         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29706         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29707         this.footerEl.hide();
29708         
29709         this.setThumbBoxSize();
29710         
29711         this.bind();
29712         
29713         this.resize();
29714         
29715         this.fireEvent('initial', this);
29716     },
29717
29718     bind : function()
29719     {
29720         var _this = this;
29721         
29722         window.addEventListener("resize", function() { _this.resize(); } );
29723         
29724         this.bodyEl.on('click', this.beforeSelectFile, this);
29725         
29726         if(Roo.isTouch){
29727             this.bodyEl.on('touchstart', this.onTouchStart, this);
29728             this.bodyEl.on('touchmove', this.onTouchMove, this);
29729             this.bodyEl.on('touchend', this.onTouchEnd, this);
29730         }
29731         
29732         if(!Roo.isTouch){
29733             this.bodyEl.on('mousedown', this.onMouseDown, this);
29734             this.bodyEl.on('mousemove', this.onMouseMove, this);
29735             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29736             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29737             Roo.get(document).on('mouseup', this.onMouseUp, this);
29738         }
29739         
29740         this.selectorEl.on('change', this.onFileSelected, this);
29741     },
29742     
29743     reset : function()
29744     {    
29745         this.scale = 0;
29746         this.baseScale = 1;
29747         this.rotate = 0;
29748         this.baseRotate = 1;
29749         this.dragable = false;
29750         this.pinching = false;
29751         this.mouseX = 0;
29752         this.mouseY = 0;
29753         this.cropData = false;
29754         this.notifyEl.dom.innerHTML = this.emptyText;
29755         
29756         this.selectorEl.dom.value = '';
29757         
29758     },
29759     
29760     resize : function()
29761     {
29762         if(this.fireEvent('resize', this) != false){
29763             this.setThumbBoxPosition();
29764             this.setCanvasPosition();
29765         }
29766     },
29767     
29768     onFooterButtonClick : function(e, el, o, type)
29769     {
29770         switch (type) {
29771             case 'rotate-left' :
29772                 this.onRotateLeft(e);
29773                 break;
29774             case 'rotate-right' :
29775                 this.onRotateRight(e);
29776                 break;
29777             case 'picture' :
29778                 this.beforeSelectFile(e);
29779                 break;
29780             case 'trash' :
29781                 this.trash(e);
29782                 break;
29783             case 'crop' :
29784                 this.crop(e);
29785                 break;
29786             case 'download' :
29787                 this.download(e);
29788                 break;
29789             default :
29790                 break;
29791         }
29792         
29793         this.fireEvent('footerbuttonclick', this, type);
29794     },
29795     
29796     beforeSelectFile : function(e)
29797     {
29798         e.preventDefault();
29799         
29800         if(this.fireEvent('beforeselectfile', this) != false){
29801             this.selectorEl.dom.click();
29802         }
29803     },
29804     
29805     onFileSelected : function(e)
29806     {
29807         e.preventDefault();
29808         
29809         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29810             return;
29811         }
29812         
29813         var file = this.selectorEl.dom.files[0];
29814         
29815         if(this.fireEvent('inspect', this, file) != false){
29816             this.prepare(file);
29817         }
29818         
29819     },
29820     
29821     trash : function(e)
29822     {
29823         this.fireEvent('trash', this);
29824     },
29825     
29826     download : function(e)
29827     {
29828         this.fireEvent('download', this);
29829     },
29830     
29831     loadCanvas : function(src)
29832     {   
29833         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29834             
29835             this.reset();
29836             
29837             this.imageEl = document.createElement('img');
29838             
29839             var _this = this;
29840             
29841             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29842             
29843             this.imageEl.src = src;
29844         }
29845     },
29846     
29847     onLoadCanvas : function()
29848     {   
29849         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29850         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29851         
29852         this.bodyEl.un('click', this.beforeSelectFile, this);
29853         
29854         this.notifyEl.hide();
29855         this.thumbEl.show();
29856         this.footerEl.show();
29857         
29858         this.baseRotateLevel();
29859         
29860         if(this.isDocument){
29861             this.setThumbBoxSize();
29862         }
29863         
29864         this.setThumbBoxPosition();
29865         
29866         this.baseScaleLevel();
29867         
29868         this.draw();
29869         
29870         this.resize();
29871         
29872         this.canvasLoaded = true;
29873         
29874         if(this.loadMask){
29875             this.maskEl.unmask();
29876         }
29877         
29878     },
29879     
29880     setCanvasPosition : function()
29881     {   
29882         if(!this.canvasEl){
29883             return;
29884         }
29885         
29886         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29887         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29888         
29889         this.previewEl.setLeft(pw);
29890         this.previewEl.setTop(ph);
29891         
29892     },
29893     
29894     onMouseDown : function(e)
29895     {   
29896         e.stopEvent();
29897         
29898         this.dragable = true;
29899         this.pinching = false;
29900         
29901         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29902             this.dragable = false;
29903             return;
29904         }
29905         
29906         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29907         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29908         
29909     },
29910     
29911     onMouseMove : function(e)
29912     {   
29913         e.stopEvent();
29914         
29915         if(!this.canvasLoaded){
29916             return;
29917         }
29918         
29919         if (!this.dragable){
29920             return;
29921         }
29922         
29923         var minX = Math.ceil(this.thumbEl.getLeft(true));
29924         var minY = Math.ceil(this.thumbEl.getTop(true));
29925         
29926         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29927         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29928         
29929         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29930         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29931         
29932         x = x - this.mouseX;
29933         y = y - this.mouseY;
29934         
29935         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29936         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29937         
29938         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29939         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29940         
29941         this.previewEl.setLeft(bgX);
29942         this.previewEl.setTop(bgY);
29943         
29944         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29945         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29946     },
29947     
29948     onMouseUp : function(e)
29949     {   
29950         e.stopEvent();
29951         
29952         this.dragable = false;
29953     },
29954     
29955     onMouseWheel : function(e)
29956     {   
29957         e.stopEvent();
29958         
29959         this.startScale = this.scale;
29960         
29961         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29962         
29963         if(!this.zoomable()){
29964             this.scale = this.startScale;
29965             return;
29966         }
29967         
29968         this.draw();
29969         
29970         return;
29971     },
29972     
29973     zoomable : function()
29974     {
29975         var minScale = this.thumbEl.getWidth() / this.minWidth;
29976         
29977         if(this.minWidth < this.minHeight){
29978             minScale = this.thumbEl.getHeight() / this.minHeight;
29979         }
29980         
29981         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29982         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29983         
29984         if(
29985                 this.isDocument &&
29986                 (this.rotate == 0 || this.rotate == 180) && 
29987                 (
29988                     width > this.imageEl.OriginWidth || 
29989                     height > this.imageEl.OriginHeight ||
29990                     (width < this.minWidth && height < this.minHeight)
29991                 )
29992         ){
29993             return false;
29994         }
29995         
29996         if(
29997                 this.isDocument &&
29998                 (this.rotate == 90 || this.rotate == 270) && 
29999                 (
30000                     width > this.imageEl.OriginWidth || 
30001                     height > this.imageEl.OriginHeight ||
30002                     (width < this.minHeight && height < this.minWidth)
30003                 )
30004         ){
30005             return false;
30006         }
30007         
30008         if(
30009                 !this.isDocument &&
30010                 (this.rotate == 0 || this.rotate == 180) && 
30011                 (
30012                     width < this.minWidth || 
30013                     width > this.imageEl.OriginWidth || 
30014                     height < this.minHeight || 
30015                     height > this.imageEl.OriginHeight
30016                 )
30017         ){
30018             return false;
30019         }
30020         
30021         if(
30022                 !this.isDocument &&
30023                 (this.rotate == 90 || this.rotate == 270) && 
30024                 (
30025                     width < this.minHeight || 
30026                     width > this.imageEl.OriginWidth || 
30027                     height < this.minWidth || 
30028                     height > this.imageEl.OriginHeight
30029                 )
30030         ){
30031             return false;
30032         }
30033         
30034         return true;
30035         
30036     },
30037     
30038     onRotateLeft : function(e)
30039     {   
30040         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30041             
30042             var minScale = this.thumbEl.getWidth() / this.minWidth;
30043             
30044             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30045             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30046             
30047             this.startScale = this.scale;
30048             
30049             while (this.getScaleLevel() < minScale){
30050             
30051                 this.scale = this.scale + 1;
30052                 
30053                 if(!this.zoomable()){
30054                     break;
30055                 }
30056                 
30057                 if(
30058                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30059                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30060                 ){
30061                     continue;
30062                 }
30063                 
30064                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30065
30066                 this.draw();
30067                 
30068                 return;
30069             }
30070             
30071             this.scale = this.startScale;
30072             
30073             this.onRotateFail();
30074             
30075             return false;
30076         }
30077         
30078         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30079
30080         if(this.isDocument){
30081             this.setThumbBoxSize();
30082             this.setThumbBoxPosition();
30083             this.setCanvasPosition();
30084         }
30085         
30086         this.draw();
30087         
30088         this.fireEvent('rotate', this, 'left');
30089         
30090     },
30091     
30092     onRotateRight : function(e)
30093     {
30094         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30095             
30096             var minScale = this.thumbEl.getWidth() / this.minWidth;
30097         
30098             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30099             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30100             
30101             this.startScale = this.scale;
30102             
30103             while (this.getScaleLevel() < minScale){
30104             
30105                 this.scale = this.scale + 1;
30106                 
30107                 if(!this.zoomable()){
30108                     break;
30109                 }
30110                 
30111                 if(
30112                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30113                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30114                 ){
30115                     continue;
30116                 }
30117                 
30118                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30119
30120                 this.draw();
30121                 
30122                 return;
30123             }
30124             
30125             this.scale = this.startScale;
30126             
30127             this.onRotateFail();
30128             
30129             return false;
30130         }
30131         
30132         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30133
30134         if(this.isDocument){
30135             this.setThumbBoxSize();
30136             this.setThumbBoxPosition();
30137             this.setCanvasPosition();
30138         }
30139         
30140         this.draw();
30141         
30142         this.fireEvent('rotate', this, 'right');
30143     },
30144     
30145     onRotateFail : function()
30146     {
30147         this.errorEl.show(true);
30148         
30149         var _this = this;
30150         
30151         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30152     },
30153     
30154     draw : function()
30155     {
30156         this.previewEl.dom.innerHTML = '';
30157         
30158         var canvasEl = document.createElement("canvas");
30159         
30160         var contextEl = canvasEl.getContext("2d");
30161         
30162         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30163         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30164         var center = this.imageEl.OriginWidth / 2;
30165         
30166         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30167             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30168             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30169             center = this.imageEl.OriginHeight / 2;
30170         }
30171         
30172         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30173         
30174         contextEl.translate(center, center);
30175         contextEl.rotate(this.rotate * Math.PI / 180);
30176
30177         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30178         
30179         this.canvasEl = document.createElement("canvas");
30180         
30181         this.contextEl = this.canvasEl.getContext("2d");
30182         
30183         switch (this.rotate) {
30184             case 0 :
30185                 
30186                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30187                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30188                 
30189                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30190                 
30191                 break;
30192             case 90 : 
30193                 
30194                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30195                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30196                 
30197                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30198                     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);
30199                     break;
30200                 }
30201                 
30202                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30203                 
30204                 break;
30205             case 180 :
30206                 
30207                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30208                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30209                 
30210                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30211                     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);
30212                     break;
30213                 }
30214                 
30215                 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);
30216                 
30217                 break;
30218             case 270 :
30219                 
30220                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30221                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30222         
30223                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30224                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30225                     break;
30226                 }
30227                 
30228                 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);
30229                 
30230                 break;
30231             default : 
30232                 break;
30233         }
30234         
30235         this.previewEl.appendChild(this.canvasEl);
30236         
30237         this.setCanvasPosition();
30238     },
30239     
30240     crop : function()
30241     {
30242         if(!this.canvasLoaded){
30243             return;
30244         }
30245         
30246         var imageCanvas = document.createElement("canvas");
30247         
30248         var imageContext = imageCanvas.getContext("2d");
30249         
30250         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30251         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30252         
30253         var center = imageCanvas.width / 2;
30254         
30255         imageContext.translate(center, center);
30256         
30257         imageContext.rotate(this.rotate * Math.PI / 180);
30258         
30259         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30260         
30261         var canvas = document.createElement("canvas");
30262         
30263         var context = canvas.getContext("2d");
30264                 
30265         canvas.width = this.minWidth;
30266         canvas.height = this.minHeight;
30267
30268         switch (this.rotate) {
30269             case 0 :
30270                 
30271                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30272                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30273                 
30274                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30275                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30276                 
30277                 var targetWidth = this.minWidth - 2 * x;
30278                 var targetHeight = this.minHeight - 2 * y;
30279                 
30280                 var scale = 1;
30281                 
30282                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30283                     scale = targetWidth / width;
30284                 }
30285                 
30286                 if(x > 0 && y == 0){
30287                     scale = targetHeight / height;
30288                 }
30289                 
30290                 if(x > 0 && y > 0){
30291                     scale = targetWidth / width;
30292                     
30293                     if(width < height){
30294                         scale = targetHeight / height;
30295                     }
30296                 }
30297                 
30298                 context.scale(scale, scale);
30299                 
30300                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30301                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30302
30303                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30304                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30305
30306                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30307                 
30308                 break;
30309             case 90 : 
30310                 
30311                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30312                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30313                 
30314                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30315                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30316                 
30317                 var targetWidth = this.minWidth - 2 * x;
30318                 var targetHeight = this.minHeight - 2 * y;
30319                 
30320                 var scale = 1;
30321                 
30322                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30323                     scale = targetWidth / width;
30324                 }
30325                 
30326                 if(x > 0 && y == 0){
30327                     scale = targetHeight / height;
30328                 }
30329                 
30330                 if(x > 0 && y > 0){
30331                     scale = targetWidth / width;
30332                     
30333                     if(width < height){
30334                         scale = targetHeight / height;
30335                     }
30336                 }
30337                 
30338                 context.scale(scale, scale);
30339                 
30340                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30341                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30342
30343                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30344                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30345                 
30346                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30347                 
30348                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30349                 
30350                 break;
30351             case 180 :
30352                 
30353                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30354                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30355                 
30356                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30357                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30358                 
30359                 var targetWidth = this.minWidth - 2 * x;
30360                 var targetHeight = this.minHeight - 2 * y;
30361                 
30362                 var scale = 1;
30363                 
30364                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30365                     scale = targetWidth / width;
30366                 }
30367                 
30368                 if(x > 0 && y == 0){
30369                     scale = targetHeight / height;
30370                 }
30371                 
30372                 if(x > 0 && y > 0){
30373                     scale = targetWidth / width;
30374                     
30375                     if(width < height){
30376                         scale = targetHeight / height;
30377                     }
30378                 }
30379                 
30380                 context.scale(scale, scale);
30381                 
30382                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30383                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30384
30385                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30386                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30387
30388                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30389                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30390                 
30391                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30392                 
30393                 break;
30394             case 270 :
30395                 
30396                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30397                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30398                 
30399                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30400                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30401                 
30402                 var targetWidth = this.minWidth - 2 * x;
30403                 var targetHeight = this.minHeight - 2 * y;
30404                 
30405                 var scale = 1;
30406                 
30407                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30408                     scale = targetWidth / width;
30409                 }
30410                 
30411                 if(x > 0 && y == 0){
30412                     scale = targetHeight / height;
30413                 }
30414                 
30415                 if(x > 0 && y > 0){
30416                     scale = targetWidth / width;
30417                     
30418                     if(width < height){
30419                         scale = targetHeight / height;
30420                     }
30421                 }
30422                 
30423                 context.scale(scale, scale);
30424                 
30425                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30426                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30427
30428                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30429                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30430                 
30431                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30432                 
30433                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30434                 
30435                 break;
30436             default : 
30437                 break;
30438         }
30439         
30440         this.cropData = canvas.toDataURL(this.cropType);
30441         
30442         if(this.fireEvent('crop', this, this.cropData) !== false){
30443             this.process(this.file, this.cropData);
30444         }
30445         
30446         return;
30447         
30448     },
30449     
30450     setThumbBoxSize : function()
30451     {
30452         var width, height;
30453         
30454         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30455             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30456             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30457             
30458             this.minWidth = width;
30459             this.minHeight = height;
30460             
30461             if(this.rotate == 90 || this.rotate == 270){
30462                 this.minWidth = height;
30463                 this.minHeight = width;
30464             }
30465         }
30466         
30467         height = 300;
30468         width = Math.ceil(this.minWidth * height / this.minHeight);
30469         
30470         if(this.minWidth > this.minHeight){
30471             width = 300;
30472             height = Math.ceil(this.minHeight * width / this.minWidth);
30473         }
30474         
30475         this.thumbEl.setStyle({
30476             width : width + 'px',
30477             height : height + 'px'
30478         });
30479
30480         return;
30481             
30482     },
30483     
30484     setThumbBoxPosition : function()
30485     {
30486         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30487         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30488         
30489         this.thumbEl.setLeft(x);
30490         this.thumbEl.setTop(y);
30491         
30492     },
30493     
30494     baseRotateLevel : function()
30495     {
30496         this.baseRotate = 1;
30497         
30498         if(
30499                 typeof(this.exif) != 'undefined' &&
30500                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30501                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30502         ){
30503             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30504         }
30505         
30506         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30507         
30508     },
30509     
30510     baseScaleLevel : function()
30511     {
30512         var width, height;
30513         
30514         if(this.isDocument){
30515             
30516             if(this.baseRotate == 6 || this.baseRotate == 8){
30517             
30518                 height = this.thumbEl.getHeight();
30519                 this.baseScale = height / this.imageEl.OriginWidth;
30520
30521                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30522                     width = this.thumbEl.getWidth();
30523                     this.baseScale = width / this.imageEl.OriginHeight;
30524                 }
30525
30526                 return;
30527             }
30528
30529             height = this.thumbEl.getHeight();
30530             this.baseScale = height / this.imageEl.OriginHeight;
30531
30532             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30533                 width = this.thumbEl.getWidth();
30534                 this.baseScale = width / this.imageEl.OriginWidth;
30535             }
30536
30537             return;
30538         }
30539         
30540         if(this.baseRotate == 6 || this.baseRotate == 8){
30541             
30542             width = this.thumbEl.getHeight();
30543             this.baseScale = width / this.imageEl.OriginHeight;
30544             
30545             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30546                 height = this.thumbEl.getWidth();
30547                 this.baseScale = height / this.imageEl.OriginHeight;
30548             }
30549             
30550             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30551                 height = this.thumbEl.getWidth();
30552                 this.baseScale = height / this.imageEl.OriginHeight;
30553                 
30554                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30555                     width = this.thumbEl.getHeight();
30556                     this.baseScale = width / this.imageEl.OriginWidth;
30557                 }
30558             }
30559             
30560             return;
30561         }
30562         
30563         width = this.thumbEl.getWidth();
30564         this.baseScale = width / this.imageEl.OriginWidth;
30565         
30566         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30567             height = this.thumbEl.getHeight();
30568             this.baseScale = height / this.imageEl.OriginHeight;
30569         }
30570         
30571         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30572             
30573             height = this.thumbEl.getHeight();
30574             this.baseScale = height / this.imageEl.OriginHeight;
30575             
30576             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30577                 width = this.thumbEl.getWidth();
30578                 this.baseScale = width / this.imageEl.OriginWidth;
30579             }
30580             
30581         }
30582         
30583         return;
30584     },
30585     
30586     getScaleLevel : function()
30587     {
30588         return this.baseScale * Math.pow(1.1, this.scale);
30589     },
30590     
30591     onTouchStart : function(e)
30592     {
30593         if(!this.canvasLoaded){
30594             this.beforeSelectFile(e);
30595             return;
30596         }
30597         
30598         var touches = e.browserEvent.touches;
30599         
30600         if(!touches){
30601             return;
30602         }
30603         
30604         if(touches.length == 1){
30605             this.onMouseDown(e);
30606             return;
30607         }
30608         
30609         if(touches.length != 2){
30610             return;
30611         }
30612         
30613         var coords = [];
30614         
30615         for(var i = 0, finger; finger = touches[i]; i++){
30616             coords.push(finger.pageX, finger.pageY);
30617         }
30618         
30619         var x = Math.pow(coords[0] - coords[2], 2);
30620         var y = Math.pow(coords[1] - coords[3], 2);
30621         
30622         this.startDistance = Math.sqrt(x + y);
30623         
30624         this.startScale = this.scale;
30625         
30626         this.pinching = true;
30627         this.dragable = false;
30628         
30629     },
30630     
30631     onTouchMove : function(e)
30632     {
30633         if(!this.pinching && !this.dragable){
30634             return;
30635         }
30636         
30637         var touches = e.browserEvent.touches;
30638         
30639         if(!touches){
30640             return;
30641         }
30642         
30643         if(this.dragable){
30644             this.onMouseMove(e);
30645             return;
30646         }
30647         
30648         var coords = [];
30649         
30650         for(var i = 0, finger; finger = touches[i]; i++){
30651             coords.push(finger.pageX, finger.pageY);
30652         }
30653         
30654         var x = Math.pow(coords[0] - coords[2], 2);
30655         var y = Math.pow(coords[1] - coords[3], 2);
30656         
30657         this.endDistance = Math.sqrt(x + y);
30658         
30659         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30660         
30661         if(!this.zoomable()){
30662             this.scale = this.startScale;
30663             return;
30664         }
30665         
30666         this.draw();
30667         
30668     },
30669     
30670     onTouchEnd : function(e)
30671     {
30672         this.pinching = false;
30673         this.dragable = false;
30674         
30675     },
30676     
30677     process : function(file, crop)
30678     {
30679         if(this.loadMask){
30680             this.maskEl.mask(this.loadingText);
30681         }
30682         
30683         this.xhr = new XMLHttpRequest();
30684         
30685         file.xhr = this.xhr;
30686
30687         this.xhr.open(this.method, this.url, true);
30688         
30689         var headers = {
30690             "Accept": "application/json",
30691             "Cache-Control": "no-cache",
30692             "X-Requested-With": "XMLHttpRequest"
30693         };
30694         
30695         for (var headerName in headers) {
30696             var headerValue = headers[headerName];
30697             if (headerValue) {
30698                 this.xhr.setRequestHeader(headerName, headerValue);
30699             }
30700         }
30701         
30702         var _this = this;
30703         
30704         this.xhr.onload = function()
30705         {
30706             _this.xhrOnLoad(_this.xhr);
30707         }
30708         
30709         this.xhr.onerror = function()
30710         {
30711             _this.xhrOnError(_this.xhr);
30712         }
30713         
30714         var formData = new FormData();
30715
30716         formData.append('returnHTML', 'NO');
30717         
30718         if(crop){
30719             formData.append('crop', crop);
30720         }
30721         
30722         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30723             formData.append(this.paramName, file, file.name);
30724         }
30725         
30726         if(typeof(file.filename) != 'undefined'){
30727             formData.append('filename', file.filename);
30728         }
30729         
30730         if(typeof(file.mimetype) != 'undefined'){
30731             formData.append('mimetype', file.mimetype);
30732         }
30733         
30734         if(this.fireEvent('arrange', this, formData) != false){
30735             this.xhr.send(formData);
30736         };
30737     },
30738     
30739     xhrOnLoad : function(xhr)
30740     {
30741         if(this.loadMask){
30742             this.maskEl.unmask();
30743         }
30744         
30745         if (xhr.readyState !== 4) {
30746             this.fireEvent('exception', this, xhr);
30747             return;
30748         }
30749
30750         var response = Roo.decode(xhr.responseText);
30751         
30752         if(!response.success){
30753             this.fireEvent('exception', this, xhr);
30754             return;
30755         }
30756         
30757         var response = Roo.decode(xhr.responseText);
30758         
30759         this.fireEvent('upload', this, response);
30760         
30761     },
30762     
30763     xhrOnError : function()
30764     {
30765         if(this.loadMask){
30766             this.maskEl.unmask();
30767         }
30768         
30769         Roo.log('xhr on error');
30770         
30771         var response = Roo.decode(xhr.responseText);
30772           
30773         Roo.log(response);
30774         
30775     },
30776     
30777     prepare : function(file)
30778     {   
30779         if(this.loadMask){
30780             this.maskEl.mask(this.loadingText);
30781         }
30782         
30783         this.file = false;
30784         this.exif = {};
30785         
30786         if(typeof(file) === 'string'){
30787             this.loadCanvas(file);
30788             return;
30789         }
30790         
30791         if(!file || !this.urlAPI){
30792             return;
30793         }
30794         
30795         this.file = file;
30796         this.cropType = file.type;
30797         
30798         var _this = this;
30799         
30800         if(this.fireEvent('prepare', this, this.file) != false){
30801             
30802             var reader = new FileReader();
30803             
30804             reader.onload = function (e) {
30805                 if (e.target.error) {
30806                     Roo.log(e.target.error);
30807                     return;
30808                 }
30809                 
30810                 var buffer = e.target.result,
30811                     dataView = new DataView(buffer),
30812                     offset = 2,
30813                     maxOffset = dataView.byteLength - 4,
30814                     markerBytes,
30815                     markerLength;
30816                 
30817                 if (dataView.getUint16(0) === 0xffd8) {
30818                     while (offset < maxOffset) {
30819                         markerBytes = dataView.getUint16(offset);
30820                         
30821                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30822                             markerLength = dataView.getUint16(offset + 2) + 2;
30823                             if (offset + markerLength > dataView.byteLength) {
30824                                 Roo.log('Invalid meta data: Invalid segment size.');
30825                                 break;
30826                             }
30827                             
30828                             if(markerBytes == 0xffe1){
30829                                 _this.parseExifData(
30830                                     dataView,
30831                                     offset,
30832                                     markerLength
30833                                 );
30834                             }
30835                             
30836                             offset += markerLength;
30837                             
30838                             continue;
30839                         }
30840                         
30841                         break;
30842                     }
30843                     
30844                 }
30845                 
30846                 var url = _this.urlAPI.createObjectURL(_this.file);
30847                 
30848                 _this.loadCanvas(url);
30849                 
30850                 return;
30851             }
30852             
30853             reader.readAsArrayBuffer(this.file);
30854             
30855         }
30856         
30857     },
30858     
30859     parseExifData : function(dataView, offset, length)
30860     {
30861         var tiffOffset = offset + 10,
30862             littleEndian,
30863             dirOffset;
30864     
30865         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30866             // No Exif data, might be XMP data instead
30867             return;
30868         }
30869         
30870         // Check for the ASCII code for "Exif" (0x45786966):
30871         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30872             // No Exif data, might be XMP data instead
30873             return;
30874         }
30875         if (tiffOffset + 8 > dataView.byteLength) {
30876             Roo.log('Invalid Exif data: Invalid segment size.');
30877             return;
30878         }
30879         // Check for the two null bytes:
30880         if (dataView.getUint16(offset + 8) !== 0x0000) {
30881             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30882             return;
30883         }
30884         // Check the byte alignment:
30885         switch (dataView.getUint16(tiffOffset)) {
30886         case 0x4949:
30887             littleEndian = true;
30888             break;
30889         case 0x4D4D:
30890             littleEndian = false;
30891             break;
30892         default:
30893             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30894             return;
30895         }
30896         // Check for the TIFF tag marker (0x002A):
30897         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30898             Roo.log('Invalid Exif data: Missing TIFF marker.');
30899             return;
30900         }
30901         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30902         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30903         
30904         this.parseExifTags(
30905             dataView,
30906             tiffOffset,
30907             tiffOffset + dirOffset,
30908             littleEndian
30909         );
30910     },
30911     
30912     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30913     {
30914         var tagsNumber,
30915             dirEndOffset,
30916             i;
30917         if (dirOffset + 6 > dataView.byteLength) {
30918             Roo.log('Invalid Exif data: Invalid directory offset.');
30919             return;
30920         }
30921         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30922         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30923         if (dirEndOffset + 4 > dataView.byteLength) {
30924             Roo.log('Invalid Exif data: Invalid directory size.');
30925             return;
30926         }
30927         for (i = 0; i < tagsNumber; i += 1) {
30928             this.parseExifTag(
30929                 dataView,
30930                 tiffOffset,
30931                 dirOffset + 2 + 12 * i, // tag offset
30932                 littleEndian
30933             );
30934         }
30935         // Return the offset to the next directory:
30936         return dataView.getUint32(dirEndOffset, littleEndian);
30937     },
30938     
30939     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30940     {
30941         var tag = dataView.getUint16(offset, littleEndian);
30942         
30943         this.exif[tag] = this.getExifValue(
30944             dataView,
30945             tiffOffset,
30946             offset,
30947             dataView.getUint16(offset + 2, littleEndian), // tag type
30948             dataView.getUint32(offset + 4, littleEndian), // tag length
30949             littleEndian
30950         );
30951     },
30952     
30953     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30954     {
30955         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30956             tagSize,
30957             dataOffset,
30958             values,
30959             i,
30960             str,
30961             c;
30962     
30963         if (!tagType) {
30964             Roo.log('Invalid Exif data: Invalid tag type.');
30965             return;
30966         }
30967         
30968         tagSize = tagType.size * length;
30969         // Determine if the value is contained in the dataOffset bytes,
30970         // or if the value at the dataOffset is a pointer to the actual data:
30971         dataOffset = tagSize > 4 ?
30972                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30973         if (dataOffset + tagSize > dataView.byteLength) {
30974             Roo.log('Invalid Exif data: Invalid data offset.');
30975             return;
30976         }
30977         if (length === 1) {
30978             return tagType.getValue(dataView, dataOffset, littleEndian);
30979         }
30980         values = [];
30981         for (i = 0; i < length; i += 1) {
30982             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30983         }
30984         
30985         if (tagType.ascii) {
30986             str = '';
30987             // Concatenate the chars:
30988             for (i = 0; i < values.length; i += 1) {
30989                 c = values[i];
30990                 // Ignore the terminating NULL byte(s):
30991                 if (c === '\u0000') {
30992                     break;
30993                 }
30994                 str += c;
30995             }
30996             return str;
30997         }
30998         return values;
30999     }
31000     
31001 });
31002
31003 Roo.apply(Roo.bootstrap.UploadCropbox, {
31004     tags : {
31005         'Orientation': 0x0112
31006     },
31007     
31008     Orientation: {
31009             1: 0, //'top-left',
31010 //            2: 'top-right',
31011             3: 180, //'bottom-right',
31012 //            4: 'bottom-left',
31013 //            5: 'left-top',
31014             6: 90, //'right-top',
31015 //            7: 'right-bottom',
31016             8: 270 //'left-bottom'
31017     },
31018     
31019     exifTagTypes : {
31020         // byte, 8-bit unsigned int:
31021         1: {
31022             getValue: function (dataView, dataOffset) {
31023                 return dataView.getUint8(dataOffset);
31024             },
31025             size: 1
31026         },
31027         // ascii, 8-bit byte:
31028         2: {
31029             getValue: function (dataView, dataOffset) {
31030                 return String.fromCharCode(dataView.getUint8(dataOffset));
31031             },
31032             size: 1,
31033             ascii: true
31034         },
31035         // short, 16 bit int:
31036         3: {
31037             getValue: function (dataView, dataOffset, littleEndian) {
31038                 return dataView.getUint16(dataOffset, littleEndian);
31039             },
31040             size: 2
31041         },
31042         // long, 32 bit int:
31043         4: {
31044             getValue: function (dataView, dataOffset, littleEndian) {
31045                 return dataView.getUint32(dataOffset, littleEndian);
31046             },
31047             size: 4
31048         },
31049         // rational = two long values, first is numerator, second is denominator:
31050         5: {
31051             getValue: function (dataView, dataOffset, littleEndian) {
31052                 return dataView.getUint32(dataOffset, littleEndian) /
31053                     dataView.getUint32(dataOffset + 4, littleEndian);
31054             },
31055             size: 8
31056         },
31057         // slong, 32 bit signed int:
31058         9: {
31059             getValue: function (dataView, dataOffset, littleEndian) {
31060                 return dataView.getInt32(dataOffset, littleEndian);
31061             },
31062             size: 4
31063         },
31064         // srational, two slongs, first is numerator, second is denominator:
31065         10: {
31066             getValue: function (dataView, dataOffset, littleEndian) {
31067                 return dataView.getInt32(dataOffset, littleEndian) /
31068                     dataView.getInt32(dataOffset + 4, littleEndian);
31069             },
31070             size: 8
31071         }
31072     },
31073     
31074     footer : {
31075         STANDARD : [
31076             {
31077                 tag : 'div',
31078                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31079                 action : 'rotate-left',
31080                 cn : [
31081                     {
31082                         tag : 'button',
31083                         cls : 'btn btn-default',
31084                         html : '<i class="fa fa-undo"></i>'
31085                     }
31086                 ]
31087             },
31088             {
31089                 tag : 'div',
31090                 cls : 'btn-group roo-upload-cropbox-picture',
31091                 action : 'picture',
31092                 cn : [
31093                     {
31094                         tag : 'button',
31095                         cls : 'btn btn-default',
31096                         html : '<i class="fa fa-picture-o"></i>'
31097                     }
31098                 ]
31099             },
31100             {
31101                 tag : 'div',
31102                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31103                 action : 'rotate-right',
31104                 cn : [
31105                     {
31106                         tag : 'button',
31107                         cls : 'btn btn-default',
31108                         html : '<i class="fa fa-repeat"></i>'
31109                     }
31110                 ]
31111             }
31112         ],
31113         DOCUMENT : [
31114             {
31115                 tag : 'div',
31116                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31117                 action : 'rotate-left',
31118                 cn : [
31119                     {
31120                         tag : 'button',
31121                         cls : 'btn btn-default',
31122                         html : '<i class="fa fa-undo"></i>'
31123                     }
31124                 ]
31125             },
31126             {
31127                 tag : 'div',
31128                 cls : 'btn-group roo-upload-cropbox-download',
31129                 action : 'download',
31130                 cn : [
31131                     {
31132                         tag : 'button',
31133                         cls : 'btn btn-default',
31134                         html : '<i class="fa fa-download"></i>'
31135                     }
31136                 ]
31137             },
31138             {
31139                 tag : 'div',
31140                 cls : 'btn-group roo-upload-cropbox-crop',
31141                 action : 'crop',
31142                 cn : [
31143                     {
31144                         tag : 'button',
31145                         cls : 'btn btn-default',
31146                         html : '<i class="fa fa-crop"></i>'
31147                     }
31148                 ]
31149             },
31150             {
31151                 tag : 'div',
31152                 cls : 'btn-group roo-upload-cropbox-trash',
31153                 action : 'trash',
31154                 cn : [
31155                     {
31156                         tag : 'button',
31157                         cls : 'btn btn-default',
31158                         html : '<i class="fa fa-trash"></i>'
31159                     }
31160                 ]
31161             },
31162             {
31163                 tag : 'div',
31164                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31165                 action : 'rotate-right',
31166                 cn : [
31167                     {
31168                         tag : 'button',
31169                         cls : 'btn btn-default',
31170                         html : '<i class="fa fa-repeat"></i>'
31171                     }
31172                 ]
31173             }
31174         ],
31175         ROTATOR : [
31176             {
31177                 tag : 'div',
31178                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31179                 action : 'rotate-left',
31180                 cn : [
31181                     {
31182                         tag : 'button',
31183                         cls : 'btn btn-default',
31184                         html : '<i class="fa fa-undo"></i>'
31185                     }
31186                 ]
31187             },
31188             {
31189                 tag : 'div',
31190                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31191                 action : 'rotate-right',
31192                 cn : [
31193                     {
31194                         tag : 'button',
31195                         cls : 'btn btn-default',
31196                         html : '<i class="fa fa-repeat"></i>'
31197                     }
31198                 ]
31199             }
31200         ]
31201     }
31202 });
31203
31204 /*
31205 * Licence: LGPL
31206 */
31207
31208 /**
31209  * @class Roo.bootstrap.DocumentManager
31210  * @extends Roo.bootstrap.Component
31211  * Bootstrap DocumentManager class
31212  * @cfg {String} paramName default 'imageUpload'
31213  * @cfg {String} toolTipName default 'filename'
31214  * @cfg {String} method default POST
31215  * @cfg {String} url action url
31216  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31217  * @cfg {Boolean} multiple multiple upload default true
31218  * @cfg {Number} thumbSize default 300
31219  * @cfg {String} fieldLabel
31220  * @cfg {Number} labelWidth default 4
31221  * @cfg {String} labelAlign (left|top) default left
31222  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31223 * @cfg {Number} labellg set the width of label (1-12)
31224  * @cfg {Number} labelmd set the width of label (1-12)
31225  * @cfg {Number} labelsm set the width of label (1-12)
31226  * @cfg {Number} labelxs set the width of label (1-12)
31227  * 
31228  * @constructor
31229  * Create a new DocumentManager
31230  * @param {Object} config The config object
31231  */
31232
31233 Roo.bootstrap.DocumentManager = function(config){
31234     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31235     
31236     this.files = [];
31237     this.delegates = [];
31238     
31239     this.addEvents({
31240         /**
31241          * @event initial
31242          * Fire when initial the DocumentManager
31243          * @param {Roo.bootstrap.DocumentManager} this
31244          */
31245         "initial" : true,
31246         /**
31247          * @event inspect
31248          * inspect selected file
31249          * @param {Roo.bootstrap.DocumentManager} this
31250          * @param {File} file
31251          */
31252         "inspect" : true,
31253         /**
31254          * @event exception
31255          * Fire when xhr load exception
31256          * @param {Roo.bootstrap.DocumentManager} this
31257          * @param {XMLHttpRequest} xhr
31258          */
31259         "exception" : true,
31260         /**
31261          * @event afterupload
31262          * Fire when xhr load exception
31263          * @param {Roo.bootstrap.DocumentManager} this
31264          * @param {XMLHttpRequest} xhr
31265          */
31266         "afterupload" : true,
31267         /**
31268          * @event prepare
31269          * prepare the form data
31270          * @param {Roo.bootstrap.DocumentManager} this
31271          * @param {Object} formData
31272          */
31273         "prepare" : true,
31274         /**
31275          * @event remove
31276          * Fire when remove the file
31277          * @param {Roo.bootstrap.DocumentManager} this
31278          * @param {Object} file
31279          */
31280         "remove" : true,
31281         /**
31282          * @event refresh
31283          * Fire after refresh the file
31284          * @param {Roo.bootstrap.DocumentManager} this
31285          */
31286         "refresh" : true,
31287         /**
31288          * @event click
31289          * Fire after click the image
31290          * @param {Roo.bootstrap.DocumentManager} this
31291          * @param {Object} file
31292          */
31293         "click" : true,
31294         /**
31295          * @event edit
31296          * Fire when upload a image and editable set to true
31297          * @param {Roo.bootstrap.DocumentManager} this
31298          * @param {Object} file
31299          */
31300         "edit" : true,
31301         /**
31302          * @event beforeselectfile
31303          * Fire before select file
31304          * @param {Roo.bootstrap.DocumentManager} this
31305          */
31306         "beforeselectfile" : true,
31307         /**
31308          * @event process
31309          * Fire before process file
31310          * @param {Roo.bootstrap.DocumentManager} this
31311          * @param {Object} file
31312          */
31313         "process" : true,
31314         /**
31315          * @event previewrendered
31316          * Fire when preview rendered
31317          * @param {Roo.bootstrap.DocumentManager} this
31318          * @param {Object} file
31319          */
31320         "previewrendered" : true,
31321         /**
31322          */
31323         "previewResize" : true
31324         
31325     });
31326 };
31327
31328 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31329     
31330     boxes : 0,
31331     inputName : '',
31332     thumbSize : 300,
31333     multiple : true,
31334     files : false,
31335     method : 'POST',
31336     url : '',
31337     paramName : 'imageUpload',
31338     toolTipName : 'filename',
31339     fieldLabel : '',
31340     labelWidth : 4,
31341     labelAlign : 'left',
31342     editable : true,
31343     delegates : false,
31344     xhr : false, 
31345     
31346     labellg : 0,
31347     labelmd : 0,
31348     labelsm : 0,
31349     labelxs : 0,
31350     
31351     getAutoCreate : function()
31352     {   
31353         var managerWidget = {
31354             tag : 'div',
31355             cls : 'roo-document-manager',
31356             cn : [
31357                 {
31358                     tag : 'input',
31359                     cls : 'roo-document-manager-selector',
31360                     type : 'file'
31361                 },
31362                 {
31363                     tag : 'div',
31364                     cls : 'roo-document-manager-uploader',
31365                     cn : [
31366                         {
31367                             tag : 'div',
31368                             cls : 'roo-document-manager-upload-btn',
31369                             html : '<i class="fa fa-plus"></i>'
31370                         }
31371                     ]
31372                     
31373                 }
31374             ]
31375         };
31376         
31377         var content = [
31378             {
31379                 tag : 'div',
31380                 cls : 'column col-md-12',
31381                 cn : managerWidget
31382             }
31383         ];
31384         
31385         if(this.fieldLabel.length){
31386             
31387             content = [
31388                 {
31389                     tag : 'div',
31390                     cls : 'column col-md-12',
31391                     html : this.fieldLabel
31392                 },
31393                 {
31394                     tag : 'div',
31395                     cls : 'column col-md-12',
31396                     cn : managerWidget
31397                 }
31398             ];
31399
31400             if(this.labelAlign == 'left'){
31401                 content = [
31402                     {
31403                         tag : 'div',
31404                         cls : 'column',
31405                         html : this.fieldLabel
31406                     },
31407                     {
31408                         tag : 'div',
31409                         cls : 'column',
31410                         cn : managerWidget
31411                     }
31412                 ];
31413                 
31414                 if(this.labelWidth > 12){
31415                     content[0].style = "width: " + this.labelWidth + 'px';
31416                 }
31417
31418                 if(this.labelWidth < 13 && this.labelmd == 0){
31419                     this.labelmd = this.labelWidth;
31420                 }
31421
31422                 if(this.labellg > 0){
31423                     content[0].cls += ' col-lg-' + this.labellg;
31424                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31425                 }
31426
31427                 if(this.labelmd > 0){
31428                     content[0].cls += ' col-md-' + this.labelmd;
31429                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31430                 }
31431
31432                 if(this.labelsm > 0){
31433                     content[0].cls += ' col-sm-' + this.labelsm;
31434                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31435                 }
31436
31437                 if(this.labelxs > 0){
31438                     content[0].cls += ' col-xs-' + this.labelxs;
31439                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31440                 }
31441                 
31442             }
31443         }
31444         
31445         var cfg = {
31446             tag : 'div',
31447             cls : 'row clearfix',
31448             cn : content
31449         };
31450         
31451         return cfg;
31452         
31453     },
31454     
31455     initEvents : function()
31456     {
31457         this.managerEl = this.el.select('.roo-document-manager', true).first();
31458         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31459         
31460         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31461         this.selectorEl.hide();
31462         
31463         if(this.multiple){
31464             this.selectorEl.attr('multiple', 'multiple');
31465         }
31466         
31467         this.selectorEl.on('change', this.onFileSelected, this);
31468         
31469         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31470         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31471         
31472         this.uploader.on('click', this.onUploaderClick, this);
31473         
31474         this.renderProgressDialog();
31475         
31476         var _this = this;
31477         
31478         window.addEventListener("resize", function() { _this.refresh(); } );
31479         
31480         this.fireEvent('initial', this);
31481     },
31482     
31483     renderProgressDialog : function()
31484     {
31485         var _this = this;
31486         
31487         this.progressDialog = new Roo.bootstrap.Modal({
31488             cls : 'roo-document-manager-progress-dialog',
31489             allow_close : false,
31490             animate : false,
31491             title : '',
31492             buttons : [
31493                 {
31494                     name  :'cancel',
31495                     weight : 'danger',
31496                     html : 'Cancel'
31497                 }
31498             ], 
31499             listeners : { 
31500                 btnclick : function() {
31501                     _this.uploadCancel();
31502                     this.hide();
31503                 }
31504             }
31505         });
31506          
31507         this.progressDialog.render(Roo.get(document.body));
31508          
31509         this.progress = new Roo.bootstrap.Progress({
31510             cls : 'roo-document-manager-progress',
31511             active : true,
31512             striped : true
31513         });
31514         
31515         this.progress.render(this.progressDialog.getChildContainer());
31516         
31517         this.progressBar = new Roo.bootstrap.ProgressBar({
31518             cls : 'roo-document-manager-progress-bar',
31519             aria_valuenow : 0,
31520             aria_valuemin : 0,
31521             aria_valuemax : 12,
31522             panel : 'success'
31523         });
31524         
31525         this.progressBar.render(this.progress.getChildContainer());
31526     },
31527     
31528     onUploaderClick : function(e)
31529     {
31530         e.preventDefault();
31531      
31532         if(this.fireEvent('beforeselectfile', this) != false){
31533             this.selectorEl.dom.click();
31534         }
31535         
31536     },
31537     
31538     onFileSelected : function(e)
31539     {
31540         e.preventDefault();
31541         
31542         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31543             return;
31544         }
31545         
31546         Roo.each(this.selectorEl.dom.files, function(file){
31547             if(this.fireEvent('inspect', this, file) != false){
31548                 this.files.push(file);
31549             }
31550         }, this);
31551         
31552         this.queue();
31553         
31554     },
31555     
31556     queue : function()
31557     {
31558         this.selectorEl.dom.value = '';
31559         
31560         if(!this.files || !this.files.length){
31561             return;
31562         }
31563         
31564         if(this.boxes > 0 && this.files.length > this.boxes){
31565             this.files = this.files.slice(0, this.boxes);
31566         }
31567         
31568         this.uploader.show();
31569         
31570         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31571             this.uploader.hide();
31572         }
31573         
31574         var _this = this;
31575         
31576         var files = [];
31577         
31578         var docs = [];
31579         
31580         Roo.each(this.files, function(file){
31581             
31582             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31583                 var f = this.renderPreview(file);
31584                 files.push(f);
31585                 return;
31586             }
31587             
31588             if(file.type.indexOf('image') != -1){
31589                 this.delegates.push(
31590                     (function(){
31591                         _this.process(file);
31592                     }).createDelegate(this)
31593                 );
31594         
31595                 return;
31596             }
31597             
31598             docs.push(
31599                 (function(){
31600                     _this.process(file);
31601                 }).createDelegate(this)
31602             );
31603             
31604         }, this);
31605         
31606         this.files = files;
31607         
31608         this.delegates = this.delegates.concat(docs);
31609         
31610         if(!this.delegates.length){
31611             this.refresh();
31612             return;
31613         }
31614         
31615         this.progressBar.aria_valuemax = this.delegates.length;
31616         
31617         this.arrange();
31618         
31619         return;
31620     },
31621     
31622     arrange : function()
31623     {
31624         if(!this.delegates.length){
31625             this.progressDialog.hide();
31626             this.refresh();
31627             return;
31628         }
31629         
31630         var delegate = this.delegates.shift();
31631         
31632         this.progressDialog.show();
31633         
31634         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31635         
31636         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31637         
31638         delegate();
31639     },
31640     
31641     refresh : function()
31642     {
31643         this.uploader.show();
31644         
31645         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31646             this.uploader.hide();
31647         }
31648         
31649         Roo.isTouch ? this.closable(false) : this.closable(true);
31650         
31651         this.fireEvent('refresh', this);
31652     },
31653     
31654     onRemove : function(e, el, o)
31655     {
31656         e.preventDefault();
31657         
31658         this.fireEvent('remove', this, o);
31659         
31660     },
31661     
31662     remove : function(o)
31663     {
31664         var files = [];
31665         
31666         Roo.each(this.files, function(file){
31667             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31668                 files.push(file);
31669                 return;
31670             }
31671
31672             o.target.remove();
31673
31674         }, this);
31675         
31676         this.files = files;
31677         
31678         this.refresh();
31679     },
31680     
31681     clear : function()
31682     {
31683         Roo.each(this.files, function(file){
31684             if(!file.target){
31685                 return;
31686             }
31687             
31688             file.target.remove();
31689
31690         }, this);
31691         
31692         this.files = [];
31693         
31694         this.refresh();
31695     },
31696     
31697     onClick : function(e, el, o)
31698     {
31699         e.preventDefault();
31700         
31701         this.fireEvent('click', this, o);
31702         
31703     },
31704     
31705     closable : function(closable)
31706     {
31707         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31708             
31709             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31710             
31711             if(closable){
31712                 el.show();
31713                 return;
31714             }
31715             
31716             el.hide();
31717             
31718         }, this);
31719     },
31720     
31721     xhrOnLoad : function(xhr)
31722     {
31723         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31724             el.remove();
31725         }, this);
31726         
31727         if (xhr.readyState !== 4) {
31728             this.arrange();
31729             this.fireEvent('exception', this, xhr);
31730             return;
31731         }
31732
31733         var response = Roo.decode(xhr.responseText);
31734         
31735         if(!response.success){
31736             this.arrange();
31737             this.fireEvent('exception', this, xhr);
31738             return;
31739         }
31740         
31741         var file = this.renderPreview(response.data);
31742         
31743         this.files.push(file);
31744         
31745         this.arrange();
31746         
31747         this.fireEvent('afterupload', this, xhr);
31748         
31749     },
31750     
31751     xhrOnError : function(xhr)
31752     {
31753         Roo.log('xhr on error');
31754         
31755         var response = Roo.decode(xhr.responseText);
31756           
31757         Roo.log(response);
31758         
31759         this.arrange();
31760     },
31761     
31762     process : function(file)
31763     {
31764         if(this.fireEvent('process', this, file) !== false){
31765             if(this.editable && file.type.indexOf('image') != -1){
31766                 this.fireEvent('edit', this, file);
31767                 return;
31768             }
31769
31770             this.uploadStart(file, false);
31771
31772             return;
31773         }
31774         
31775     },
31776     
31777     uploadStart : function(file, crop)
31778     {
31779         this.xhr = new XMLHttpRequest();
31780         
31781         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31782             this.arrange();
31783             return;
31784         }
31785         
31786         file.xhr = this.xhr;
31787             
31788         this.managerEl.createChild({
31789             tag : 'div',
31790             cls : 'roo-document-manager-loading',
31791             cn : [
31792                 {
31793                     tag : 'div',
31794                     tooltip : file.name,
31795                     cls : 'roo-document-manager-thumb',
31796                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31797                 }
31798             ]
31799
31800         });
31801
31802         this.xhr.open(this.method, this.url, true);
31803         
31804         var headers = {
31805             "Accept": "application/json",
31806             "Cache-Control": "no-cache",
31807             "X-Requested-With": "XMLHttpRequest"
31808         };
31809         
31810         for (var headerName in headers) {
31811             var headerValue = headers[headerName];
31812             if (headerValue) {
31813                 this.xhr.setRequestHeader(headerName, headerValue);
31814             }
31815         }
31816         
31817         var _this = this;
31818         
31819         this.xhr.onload = function()
31820         {
31821             _this.xhrOnLoad(_this.xhr);
31822         }
31823         
31824         this.xhr.onerror = function()
31825         {
31826             _this.xhrOnError(_this.xhr);
31827         }
31828         
31829         var formData = new FormData();
31830
31831         formData.append('returnHTML', 'NO');
31832         
31833         if(crop){
31834             formData.append('crop', crop);
31835         }
31836         
31837         formData.append(this.paramName, file, file.name);
31838         
31839         var options = {
31840             file : file, 
31841             manually : false
31842         };
31843         
31844         if(this.fireEvent('prepare', this, formData, options) != false){
31845             
31846             if(options.manually){
31847                 return;
31848             }
31849             
31850             this.xhr.send(formData);
31851             return;
31852         };
31853         
31854         this.uploadCancel();
31855     },
31856     
31857     uploadCancel : function()
31858     {
31859         if (this.xhr) {
31860             this.xhr.abort();
31861         }
31862         
31863         this.delegates = [];
31864         
31865         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31866             el.remove();
31867         }, this);
31868         
31869         this.arrange();
31870     },
31871     
31872     renderPreview : function(file)
31873     {
31874         if(typeof(file.target) != 'undefined' && file.target){
31875             return file;
31876         }
31877         
31878         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31879         
31880         var previewEl = this.managerEl.createChild({
31881             tag : 'div',
31882             cls : 'roo-document-manager-preview',
31883             cn : [
31884                 {
31885                     tag : 'div',
31886                     tooltip : file[this.toolTipName],
31887                     cls : 'roo-document-manager-thumb',
31888                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31889                 },
31890                 {
31891                     tag : 'button',
31892                     cls : 'close',
31893                     html : '<i class="fa fa-times-circle"></i>'
31894                 }
31895             ]
31896         });
31897
31898         var close = previewEl.select('button.close', true).first();
31899
31900         close.on('click', this.onRemove, this, file);
31901
31902         file.target = previewEl;
31903
31904         var image = previewEl.select('img', true).first();
31905         
31906         var _this = this;
31907         
31908         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31909         
31910         image.on('click', this.onClick, this, file);
31911         
31912         this.fireEvent('previewrendered', this, file);
31913         
31914         return file;
31915         
31916     },
31917     
31918     onPreviewLoad : function(file, image)
31919     {
31920         if(typeof(file.target) == 'undefined' || !file.target){
31921             return;
31922         }
31923         
31924         var width = image.dom.naturalWidth || image.dom.width;
31925         var height = image.dom.naturalHeight || image.dom.height;
31926         
31927         if(!this.previewResize) {
31928             return;
31929         }
31930         
31931         if(width > height){
31932             file.target.addClass('wide');
31933             return;
31934         }
31935         
31936         file.target.addClass('tall');
31937         return;
31938         
31939     },
31940     
31941     uploadFromSource : function(file, crop)
31942     {
31943         this.xhr = new XMLHttpRequest();
31944         
31945         this.managerEl.createChild({
31946             tag : 'div',
31947             cls : 'roo-document-manager-loading',
31948             cn : [
31949                 {
31950                     tag : 'div',
31951                     tooltip : file.name,
31952                     cls : 'roo-document-manager-thumb',
31953                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31954                 }
31955             ]
31956
31957         });
31958
31959         this.xhr.open(this.method, this.url, true);
31960         
31961         var headers = {
31962             "Accept": "application/json",
31963             "Cache-Control": "no-cache",
31964             "X-Requested-With": "XMLHttpRequest"
31965         };
31966         
31967         for (var headerName in headers) {
31968             var headerValue = headers[headerName];
31969             if (headerValue) {
31970                 this.xhr.setRequestHeader(headerName, headerValue);
31971             }
31972         }
31973         
31974         var _this = this;
31975         
31976         this.xhr.onload = function()
31977         {
31978             _this.xhrOnLoad(_this.xhr);
31979         }
31980         
31981         this.xhr.onerror = function()
31982         {
31983             _this.xhrOnError(_this.xhr);
31984         }
31985         
31986         var formData = new FormData();
31987
31988         formData.append('returnHTML', 'NO');
31989         
31990         formData.append('crop', crop);
31991         
31992         if(typeof(file.filename) != 'undefined'){
31993             formData.append('filename', file.filename);
31994         }
31995         
31996         if(typeof(file.mimetype) != 'undefined'){
31997             formData.append('mimetype', file.mimetype);
31998         }
31999         
32000         Roo.log(formData);
32001         
32002         if(this.fireEvent('prepare', this, formData) != false){
32003             this.xhr.send(formData);
32004         };
32005     }
32006 });
32007
32008 /*
32009 * Licence: LGPL
32010 */
32011
32012 /**
32013  * @class Roo.bootstrap.DocumentViewer
32014  * @extends Roo.bootstrap.Component
32015  * Bootstrap DocumentViewer class
32016  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32017  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32018  * 
32019  * @constructor
32020  * Create a new DocumentViewer
32021  * @param {Object} config The config object
32022  */
32023
32024 Roo.bootstrap.DocumentViewer = function(config){
32025     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32026     
32027     this.addEvents({
32028         /**
32029          * @event initial
32030          * Fire after initEvent
32031          * @param {Roo.bootstrap.DocumentViewer} this
32032          */
32033         "initial" : true,
32034         /**
32035          * @event click
32036          * Fire after click
32037          * @param {Roo.bootstrap.DocumentViewer} this
32038          */
32039         "click" : true,
32040         /**
32041          * @event download
32042          * Fire after download button
32043          * @param {Roo.bootstrap.DocumentViewer} this
32044          */
32045         "download" : true,
32046         /**
32047          * @event trash
32048          * Fire after trash button
32049          * @param {Roo.bootstrap.DocumentViewer} this
32050          */
32051         "trash" : true
32052         
32053     });
32054 };
32055
32056 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32057     
32058     showDownload : true,
32059     
32060     showTrash : true,
32061     
32062     getAutoCreate : function()
32063     {
32064         var cfg = {
32065             tag : 'div',
32066             cls : 'roo-document-viewer',
32067             cn : [
32068                 {
32069                     tag : 'div',
32070                     cls : 'roo-document-viewer-body',
32071                     cn : [
32072                         {
32073                             tag : 'div',
32074                             cls : 'roo-document-viewer-thumb',
32075                             cn : [
32076                                 {
32077                                     tag : 'img',
32078                                     cls : 'roo-document-viewer-image'
32079                                 }
32080                             ]
32081                         }
32082                     ]
32083                 },
32084                 {
32085                     tag : 'div',
32086                     cls : 'roo-document-viewer-footer',
32087                     cn : {
32088                         tag : 'div',
32089                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32090                         cn : [
32091                             {
32092                                 tag : 'div',
32093                                 cls : 'btn-group roo-document-viewer-download',
32094                                 cn : [
32095                                     {
32096                                         tag : 'button',
32097                                         cls : 'btn btn-default',
32098                                         html : '<i class="fa fa-download"></i>'
32099                                     }
32100                                 ]
32101                             },
32102                             {
32103                                 tag : 'div',
32104                                 cls : 'btn-group roo-document-viewer-trash',
32105                                 cn : [
32106                                     {
32107                                         tag : 'button',
32108                                         cls : 'btn btn-default',
32109                                         html : '<i class="fa fa-trash"></i>'
32110                                     }
32111                                 ]
32112                             }
32113                         ]
32114                     }
32115                 }
32116             ]
32117         };
32118         
32119         return cfg;
32120     },
32121     
32122     initEvents : function()
32123     {
32124         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32125         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32126         
32127         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32128         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32129         
32130         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32131         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32132         
32133         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32134         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32135         
32136         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32137         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32138         
32139         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32140         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32141         
32142         this.bodyEl.on('click', this.onClick, this);
32143         this.downloadBtn.on('click', this.onDownload, this);
32144         this.trashBtn.on('click', this.onTrash, this);
32145         
32146         this.downloadBtn.hide();
32147         this.trashBtn.hide();
32148         
32149         if(this.showDownload){
32150             this.downloadBtn.show();
32151         }
32152         
32153         if(this.showTrash){
32154             this.trashBtn.show();
32155         }
32156         
32157         if(!this.showDownload && !this.showTrash) {
32158             this.footerEl.hide();
32159         }
32160         
32161     },
32162     
32163     initial : function()
32164     {
32165         this.fireEvent('initial', this);
32166         
32167     },
32168     
32169     onClick : function(e)
32170     {
32171         e.preventDefault();
32172         
32173         this.fireEvent('click', this);
32174     },
32175     
32176     onDownload : function(e)
32177     {
32178         e.preventDefault();
32179         
32180         this.fireEvent('download', this);
32181     },
32182     
32183     onTrash : function(e)
32184     {
32185         e.preventDefault();
32186         
32187         this.fireEvent('trash', this);
32188     }
32189     
32190 });
32191 /*
32192  * - LGPL
32193  *
32194  * nav progress bar
32195  * 
32196  */
32197
32198 /**
32199  * @class Roo.bootstrap.NavProgressBar
32200  * @extends Roo.bootstrap.Component
32201  * Bootstrap NavProgressBar class
32202  * 
32203  * @constructor
32204  * Create a new nav progress bar
32205  * @param {Object} config The config object
32206  */
32207
32208 Roo.bootstrap.NavProgressBar = function(config){
32209     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32210
32211     this.bullets = this.bullets || [];
32212    
32213 //    Roo.bootstrap.NavProgressBar.register(this);
32214      this.addEvents({
32215         /**
32216              * @event changed
32217              * Fires when the active item changes
32218              * @param {Roo.bootstrap.NavProgressBar} this
32219              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32220              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32221          */
32222         'changed': true
32223      });
32224     
32225 };
32226
32227 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32228     
32229     bullets : [],
32230     barItems : [],
32231     
32232     getAutoCreate : function()
32233     {
32234         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32235         
32236         cfg = {
32237             tag : 'div',
32238             cls : 'roo-navigation-bar-group',
32239             cn : [
32240                 {
32241                     tag : 'div',
32242                     cls : 'roo-navigation-top-bar'
32243                 },
32244                 {
32245                     tag : 'div',
32246                     cls : 'roo-navigation-bullets-bar',
32247                     cn : [
32248                         {
32249                             tag : 'ul',
32250                             cls : 'roo-navigation-bar'
32251                         }
32252                     ]
32253                 },
32254                 
32255                 {
32256                     tag : 'div',
32257                     cls : 'roo-navigation-bottom-bar'
32258                 }
32259             ]
32260             
32261         };
32262         
32263         return cfg;
32264         
32265     },
32266     
32267     initEvents: function() 
32268     {
32269         
32270     },
32271     
32272     onRender : function(ct, position) 
32273     {
32274         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32275         
32276         if(this.bullets.length){
32277             Roo.each(this.bullets, function(b){
32278                this.addItem(b);
32279             }, this);
32280         }
32281         
32282         this.format();
32283         
32284     },
32285     
32286     addItem : function(cfg)
32287     {
32288         var item = new Roo.bootstrap.NavProgressItem(cfg);
32289         
32290         item.parentId = this.id;
32291         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32292         
32293         if(cfg.html){
32294             var top = new Roo.bootstrap.Element({
32295                 tag : 'div',
32296                 cls : 'roo-navigation-bar-text'
32297             });
32298             
32299             var bottom = new Roo.bootstrap.Element({
32300                 tag : 'div',
32301                 cls : 'roo-navigation-bar-text'
32302             });
32303             
32304             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32305             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32306             
32307             var topText = new Roo.bootstrap.Element({
32308                 tag : 'span',
32309                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32310             });
32311             
32312             var bottomText = new Roo.bootstrap.Element({
32313                 tag : 'span',
32314                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32315             });
32316             
32317             topText.onRender(top.el, null);
32318             bottomText.onRender(bottom.el, null);
32319             
32320             item.topEl = top;
32321             item.bottomEl = bottom;
32322         }
32323         
32324         this.barItems.push(item);
32325         
32326         return item;
32327     },
32328     
32329     getActive : function()
32330     {
32331         var active = false;
32332         
32333         Roo.each(this.barItems, function(v){
32334             
32335             if (!v.isActive()) {
32336                 return;
32337             }
32338             
32339             active = v;
32340             return false;
32341             
32342         });
32343         
32344         return active;
32345     },
32346     
32347     setActiveItem : function(item)
32348     {
32349         var prev = false;
32350         
32351         Roo.each(this.barItems, function(v){
32352             if (v.rid == item.rid) {
32353                 return ;
32354             }
32355             
32356             if (v.isActive()) {
32357                 v.setActive(false);
32358                 prev = v;
32359             }
32360         });
32361
32362         item.setActive(true);
32363         
32364         this.fireEvent('changed', this, item, prev);
32365     },
32366     
32367     getBarItem: function(rid)
32368     {
32369         var ret = false;
32370         
32371         Roo.each(this.barItems, function(e) {
32372             if (e.rid != rid) {
32373                 return;
32374             }
32375             
32376             ret =  e;
32377             return false;
32378         });
32379         
32380         return ret;
32381     },
32382     
32383     indexOfItem : function(item)
32384     {
32385         var index = false;
32386         
32387         Roo.each(this.barItems, function(v, i){
32388             
32389             if (v.rid != item.rid) {
32390                 return;
32391             }
32392             
32393             index = i;
32394             return false
32395         });
32396         
32397         return index;
32398     },
32399     
32400     setActiveNext : function()
32401     {
32402         var i = this.indexOfItem(this.getActive());
32403         
32404         if (i > this.barItems.length) {
32405             return;
32406         }
32407         
32408         this.setActiveItem(this.barItems[i+1]);
32409     },
32410     
32411     setActivePrev : function()
32412     {
32413         var i = this.indexOfItem(this.getActive());
32414         
32415         if (i  < 1) {
32416             return;
32417         }
32418         
32419         this.setActiveItem(this.barItems[i-1]);
32420     },
32421     
32422     format : function()
32423     {
32424         if(!this.barItems.length){
32425             return;
32426         }
32427      
32428         var width = 100 / this.barItems.length;
32429         
32430         Roo.each(this.barItems, function(i){
32431             i.el.setStyle('width', width + '%');
32432             i.topEl.el.setStyle('width', width + '%');
32433             i.bottomEl.el.setStyle('width', width + '%');
32434         }, this);
32435         
32436     }
32437     
32438 });
32439 /*
32440  * - LGPL
32441  *
32442  * Nav Progress Item
32443  * 
32444  */
32445
32446 /**
32447  * @class Roo.bootstrap.NavProgressItem
32448  * @extends Roo.bootstrap.Component
32449  * Bootstrap NavProgressItem class
32450  * @cfg {String} rid the reference id
32451  * @cfg {Boolean} active (true|false) Is item active default false
32452  * @cfg {Boolean} disabled (true|false) Is item active default false
32453  * @cfg {String} html
32454  * @cfg {String} position (top|bottom) text position default bottom
32455  * @cfg {String} icon show icon instead of number
32456  * 
32457  * @constructor
32458  * Create a new NavProgressItem
32459  * @param {Object} config The config object
32460  */
32461 Roo.bootstrap.NavProgressItem = function(config){
32462     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32463     this.addEvents({
32464         // raw events
32465         /**
32466          * @event click
32467          * The raw click event for the entire grid.
32468          * @param {Roo.bootstrap.NavProgressItem} this
32469          * @param {Roo.EventObject} e
32470          */
32471         "click" : true
32472     });
32473    
32474 };
32475
32476 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32477     
32478     rid : '',
32479     active : false,
32480     disabled : false,
32481     html : '',
32482     position : 'bottom',
32483     icon : false,
32484     
32485     getAutoCreate : function()
32486     {
32487         var iconCls = 'roo-navigation-bar-item-icon';
32488         
32489         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32490         
32491         var cfg = {
32492             tag: 'li',
32493             cls: 'roo-navigation-bar-item',
32494             cn : [
32495                 {
32496                     tag : 'i',
32497                     cls : iconCls
32498                 }
32499             ]
32500         };
32501         
32502         if(this.active){
32503             cfg.cls += ' active';
32504         }
32505         if(this.disabled){
32506             cfg.cls += ' disabled';
32507         }
32508         
32509         return cfg;
32510     },
32511     
32512     disable : function()
32513     {
32514         this.setDisabled(true);
32515     },
32516     
32517     enable : function()
32518     {
32519         this.setDisabled(false);
32520     },
32521     
32522     initEvents: function() 
32523     {
32524         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32525         
32526         this.iconEl.on('click', this.onClick, this);
32527     },
32528     
32529     onClick : function(e)
32530     {
32531         e.preventDefault();
32532         
32533         if(this.disabled){
32534             return;
32535         }
32536         
32537         if(this.fireEvent('click', this, e) === false){
32538             return;
32539         };
32540         
32541         this.parent().setActiveItem(this);
32542     },
32543     
32544     isActive: function () 
32545     {
32546         return this.active;
32547     },
32548     
32549     setActive : function(state)
32550     {
32551         if(this.active == state){
32552             return;
32553         }
32554         
32555         this.active = state;
32556         
32557         if (state) {
32558             this.el.addClass('active');
32559             return;
32560         }
32561         
32562         this.el.removeClass('active');
32563         
32564         return;
32565     },
32566     
32567     setDisabled : function(state)
32568     {
32569         if(this.disabled == state){
32570             return;
32571         }
32572         
32573         this.disabled = state;
32574         
32575         if (state) {
32576             this.el.addClass('disabled');
32577             return;
32578         }
32579         
32580         this.el.removeClass('disabled');
32581     },
32582     
32583     tooltipEl : function()
32584     {
32585         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32586     }
32587 });
32588  
32589
32590  /*
32591  * - LGPL
32592  *
32593  * FieldLabel
32594  * 
32595  */
32596
32597 /**
32598  * @class Roo.bootstrap.FieldLabel
32599  * @extends Roo.bootstrap.Component
32600  * Bootstrap FieldLabel class
32601  * @cfg {String} html contents of the element
32602  * @cfg {String} tag tag of the element default label
32603  * @cfg {String} cls class of the element
32604  * @cfg {String} target label target 
32605  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32606  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32607  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32608  * @cfg {String} iconTooltip default "This field is required"
32609  * @cfg {String} indicatorpos (left|right) default left
32610  * 
32611  * @constructor
32612  * Create a new FieldLabel
32613  * @param {Object} config The config object
32614  */
32615
32616 Roo.bootstrap.FieldLabel = function(config){
32617     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32618     
32619     this.addEvents({
32620             /**
32621              * @event invalid
32622              * Fires after the field has been marked as invalid.
32623              * @param {Roo.form.FieldLabel} this
32624              * @param {String} msg The validation message
32625              */
32626             invalid : true,
32627             /**
32628              * @event valid
32629              * Fires after the field has been validated with no errors.
32630              * @param {Roo.form.FieldLabel} this
32631              */
32632             valid : true
32633         });
32634 };
32635
32636 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32637     
32638     tag: 'label',
32639     cls: '',
32640     html: '',
32641     target: '',
32642     allowBlank : true,
32643     invalidClass : 'has-warning',
32644     validClass : 'has-success',
32645     iconTooltip : 'This field is required',
32646     indicatorpos : 'left',
32647     
32648     getAutoCreate : function(){
32649         
32650         var cls = "";
32651         if (!this.allowBlank) {
32652             cls  = "visible";
32653         }
32654         
32655         var cfg = {
32656             tag : this.tag,
32657             cls : 'roo-bootstrap-field-label ' + this.cls,
32658             for : this.target,
32659             cn : [
32660                 {
32661                     tag : 'i',
32662                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32663                     tooltip : this.iconTooltip
32664                 },
32665                 {
32666                     tag : 'span',
32667                     html : this.html
32668                 }
32669             ] 
32670         };
32671         
32672         if(this.indicatorpos == 'right'){
32673             var cfg = {
32674                 tag : this.tag,
32675                 cls : 'roo-bootstrap-field-label ' + this.cls,
32676                 for : this.target,
32677                 cn : [
32678                     {
32679                         tag : 'span',
32680                         html : this.html
32681                     },
32682                     {
32683                         tag : 'i',
32684                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32685                         tooltip : this.iconTooltip
32686                     }
32687                 ] 
32688             };
32689         }
32690         
32691         return cfg;
32692     },
32693     
32694     initEvents: function() 
32695     {
32696         Roo.bootstrap.Element.superclass.initEvents.call(this);
32697         
32698         this.indicator = this.indicatorEl();
32699         
32700         if(this.indicator){
32701             this.indicator.removeClass('visible');
32702             this.indicator.addClass('invisible');
32703         }
32704         
32705         Roo.bootstrap.FieldLabel.register(this);
32706     },
32707     
32708     indicatorEl : function()
32709     {
32710         var indicator = this.el.select('i.roo-required-indicator',true).first();
32711         
32712         if(!indicator){
32713             return false;
32714         }
32715         
32716         return indicator;
32717         
32718     },
32719     
32720     /**
32721      * Mark this field as valid
32722      */
32723     markValid : function()
32724     {
32725         if(this.indicator){
32726             this.indicator.removeClass('visible');
32727             this.indicator.addClass('invisible');
32728         }
32729         if (Roo.bootstrap.version == 3) {
32730             this.el.removeClass(this.invalidClass);
32731             this.el.addClass(this.validClass);
32732         } else {
32733             this.el.removeClass('is-invalid');
32734             this.el.addClass('is-valid');
32735         }
32736         
32737         
32738         this.fireEvent('valid', this);
32739     },
32740     
32741     /**
32742      * Mark this field as invalid
32743      * @param {String} msg The validation message
32744      */
32745     markInvalid : function(msg)
32746     {
32747         if(this.indicator){
32748             this.indicator.removeClass('invisible');
32749             this.indicator.addClass('visible');
32750         }
32751           if (Roo.bootstrap.version == 3) {
32752             this.el.removeClass(this.validClass);
32753             this.el.addClass(this.invalidClass);
32754         } else {
32755             this.el.removeClass('is-valid');
32756             this.el.addClass('is-invalid');
32757         }
32758         
32759         
32760         this.fireEvent('invalid', this, msg);
32761     }
32762     
32763    
32764 });
32765
32766 Roo.apply(Roo.bootstrap.FieldLabel, {
32767     
32768     groups: {},
32769     
32770      /**
32771     * register a FieldLabel Group
32772     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32773     */
32774     register : function(label)
32775     {
32776         if(this.groups.hasOwnProperty(label.target)){
32777             return;
32778         }
32779      
32780         this.groups[label.target] = label;
32781         
32782     },
32783     /**
32784     * fetch a FieldLabel Group based on the target
32785     * @param {string} target
32786     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32787     */
32788     get: function(target) {
32789         if (typeof(this.groups[target]) == 'undefined') {
32790             return false;
32791         }
32792         
32793         return this.groups[target] ;
32794     }
32795 });
32796
32797  
32798
32799  /*
32800  * - LGPL
32801  *
32802  * page DateSplitField.
32803  * 
32804  */
32805
32806
32807 /**
32808  * @class Roo.bootstrap.DateSplitField
32809  * @extends Roo.bootstrap.Component
32810  * Bootstrap DateSplitField class
32811  * @cfg {string} fieldLabel - the label associated
32812  * @cfg {Number} labelWidth set the width of label (0-12)
32813  * @cfg {String} labelAlign (top|left)
32814  * @cfg {Boolean} dayAllowBlank (true|false) default false
32815  * @cfg {Boolean} monthAllowBlank (true|false) default false
32816  * @cfg {Boolean} yearAllowBlank (true|false) default false
32817  * @cfg {string} dayPlaceholder 
32818  * @cfg {string} monthPlaceholder
32819  * @cfg {string} yearPlaceholder
32820  * @cfg {string} dayFormat default 'd'
32821  * @cfg {string} monthFormat default 'm'
32822  * @cfg {string} yearFormat default 'Y'
32823  * @cfg {Number} labellg set the width of label (1-12)
32824  * @cfg {Number} labelmd set the width of label (1-12)
32825  * @cfg {Number} labelsm set the width of label (1-12)
32826  * @cfg {Number} labelxs set the width of label (1-12)
32827
32828  *     
32829  * @constructor
32830  * Create a new DateSplitField
32831  * @param {Object} config The config object
32832  */
32833
32834 Roo.bootstrap.DateSplitField = function(config){
32835     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32836     
32837     this.addEvents({
32838         // raw events
32839          /**
32840          * @event years
32841          * getting the data of years
32842          * @param {Roo.bootstrap.DateSplitField} this
32843          * @param {Object} years
32844          */
32845         "years" : true,
32846         /**
32847          * @event days
32848          * getting the data of days
32849          * @param {Roo.bootstrap.DateSplitField} this
32850          * @param {Object} days
32851          */
32852         "days" : true,
32853         /**
32854          * @event invalid
32855          * Fires after the field has been marked as invalid.
32856          * @param {Roo.form.Field} this
32857          * @param {String} msg The validation message
32858          */
32859         invalid : true,
32860        /**
32861          * @event valid
32862          * Fires after the field has been validated with no errors.
32863          * @param {Roo.form.Field} this
32864          */
32865         valid : true
32866     });
32867 };
32868
32869 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32870     
32871     fieldLabel : '',
32872     labelAlign : 'top',
32873     labelWidth : 3,
32874     dayAllowBlank : false,
32875     monthAllowBlank : false,
32876     yearAllowBlank : false,
32877     dayPlaceholder : '',
32878     monthPlaceholder : '',
32879     yearPlaceholder : '',
32880     dayFormat : 'd',
32881     monthFormat : 'm',
32882     yearFormat : 'Y',
32883     isFormField : true,
32884     labellg : 0,
32885     labelmd : 0,
32886     labelsm : 0,
32887     labelxs : 0,
32888     
32889     getAutoCreate : function()
32890     {
32891         var cfg = {
32892             tag : 'div',
32893             cls : 'row roo-date-split-field-group',
32894             cn : [
32895                 {
32896                     tag : 'input',
32897                     type : 'hidden',
32898                     cls : 'form-hidden-field roo-date-split-field-group-value',
32899                     name : this.name
32900                 }
32901             ]
32902         };
32903         
32904         var labelCls = 'col-md-12';
32905         var contentCls = 'col-md-4';
32906         
32907         if(this.fieldLabel){
32908             
32909             var label = {
32910                 tag : 'div',
32911                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32912                 cn : [
32913                     {
32914                         tag : 'label',
32915                         html : this.fieldLabel
32916                     }
32917                 ]
32918             };
32919             
32920             if(this.labelAlign == 'left'){
32921             
32922                 if(this.labelWidth > 12){
32923                     label.style = "width: " + this.labelWidth + 'px';
32924                 }
32925
32926                 if(this.labelWidth < 13 && this.labelmd == 0){
32927                     this.labelmd = this.labelWidth;
32928                 }
32929
32930                 if(this.labellg > 0){
32931                     labelCls = ' col-lg-' + this.labellg;
32932                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32933                 }
32934
32935                 if(this.labelmd > 0){
32936                     labelCls = ' col-md-' + this.labelmd;
32937                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32938                 }
32939
32940                 if(this.labelsm > 0){
32941                     labelCls = ' col-sm-' + this.labelsm;
32942                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32943                 }
32944
32945                 if(this.labelxs > 0){
32946                     labelCls = ' col-xs-' + this.labelxs;
32947                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32948                 }
32949             }
32950             
32951             label.cls += ' ' + labelCls;
32952             
32953             cfg.cn.push(label);
32954         }
32955         
32956         Roo.each(['day', 'month', 'year'], function(t){
32957             cfg.cn.push({
32958                 tag : 'div',
32959                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32960             });
32961         }, this);
32962         
32963         return cfg;
32964     },
32965     
32966     inputEl: function ()
32967     {
32968         return this.el.select('.roo-date-split-field-group-value', true).first();
32969     },
32970     
32971     onRender : function(ct, position) 
32972     {
32973         var _this = this;
32974         
32975         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32976         
32977         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32978         
32979         this.dayField = new Roo.bootstrap.ComboBox({
32980             allowBlank : this.dayAllowBlank,
32981             alwaysQuery : true,
32982             displayField : 'value',
32983             editable : false,
32984             fieldLabel : '',
32985             forceSelection : true,
32986             mode : 'local',
32987             placeholder : this.dayPlaceholder,
32988             selectOnFocus : true,
32989             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32990             triggerAction : 'all',
32991             typeAhead : true,
32992             valueField : 'value',
32993             store : new Roo.data.SimpleStore({
32994                 data : (function() {    
32995                     var days = [];
32996                     _this.fireEvent('days', _this, days);
32997                     return days;
32998                 })(),
32999                 fields : [ 'value' ]
33000             }),
33001             listeners : {
33002                 select : function (_self, record, index)
33003                 {
33004                     _this.setValue(_this.getValue());
33005                 }
33006             }
33007         });
33008
33009         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33010         
33011         this.monthField = new Roo.bootstrap.MonthField({
33012             after : '<i class=\"fa fa-calendar\"></i>',
33013             allowBlank : this.monthAllowBlank,
33014             placeholder : this.monthPlaceholder,
33015             readOnly : true,
33016             listeners : {
33017                 render : function (_self)
33018                 {
33019                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33020                         e.preventDefault();
33021                         _self.focus();
33022                     });
33023                 },
33024                 select : function (_self, oldvalue, newvalue)
33025                 {
33026                     _this.setValue(_this.getValue());
33027                 }
33028             }
33029         });
33030         
33031         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33032         
33033         this.yearField = new Roo.bootstrap.ComboBox({
33034             allowBlank : this.yearAllowBlank,
33035             alwaysQuery : true,
33036             displayField : 'value',
33037             editable : false,
33038             fieldLabel : '',
33039             forceSelection : true,
33040             mode : 'local',
33041             placeholder : this.yearPlaceholder,
33042             selectOnFocus : true,
33043             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33044             triggerAction : 'all',
33045             typeAhead : true,
33046             valueField : 'value',
33047             store : new Roo.data.SimpleStore({
33048                 data : (function() {
33049                     var years = [];
33050                     _this.fireEvent('years', _this, years);
33051                     return years;
33052                 })(),
33053                 fields : [ 'value' ]
33054             }),
33055             listeners : {
33056                 select : function (_self, record, index)
33057                 {
33058                     _this.setValue(_this.getValue());
33059                 }
33060             }
33061         });
33062
33063         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33064     },
33065     
33066     setValue : function(v, format)
33067     {
33068         this.inputEl.dom.value = v;
33069         
33070         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33071         
33072         var d = Date.parseDate(v, f);
33073         
33074         if(!d){
33075             this.validate();
33076             return;
33077         }
33078         
33079         this.setDay(d.format(this.dayFormat));
33080         this.setMonth(d.format(this.monthFormat));
33081         this.setYear(d.format(this.yearFormat));
33082         
33083         this.validate();
33084         
33085         return;
33086     },
33087     
33088     setDay : function(v)
33089     {
33090         this.dayField.setValue(v);
33091         this.inputEl.dom.value = this.getValue();
33092         this.validate();
33093         return;
33094     },
33095     
33096     setMonth : function(v)
33097     {
33098         this.monthField.setValue(v, true);
33099         this.inputEl.dom.value = this.getValue();
33100         this.validate();
33101         return;
33102     },
33103     
33104     setYear : function(v)
33105     {
33106         this.yearField.setValue(v);
33107         this.inputEl.dom.value = this.getValue();
33108         this.validate();
33109         return;
33110     },
33111     
33112     getDay : function()
33113     {
33114         return this.dayField.getValue();
33115     },
33116     
33117     getMonth : function()
33118     {
33119         return this.monthField.getValue();
33120     },
33121     
33122     getYear : function()
33123     {
33124         return this.yearField.getValue();
33125     },
33126     
33127     getValue : function()
33128     {
33129         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33130         
33131         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33132         
33133         return date;
33134     },
33135     
33136     reset : function()
33137     {
33138         this.setDay('');
33139         this.setMonth('');
33140         this.setYear('');
33141         this.inputEl.dom.value = '';
33142         this.validate();
33143         return;
33144     },
33145     
33146     validate : function()
33147     {
33148         var d = this.dayField.validate();
33149         var m = this.monthField.validate();
33150         var y = this.yearField.validate();
33151         
33152         var valid = true;
33153         
33154         if(
33155                 (!this.dayAllowBlank && !d) ||
33156                 (!this.monthAllowBlank && !m) ||
33157                 (!this.yearAllowBlank && !y)
33158         ){
33159             valid = false;
33160         }
33161         
33162         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33163             return valid;
33164         }
33165         
33166         if(valid){
33167             this.markValid();
33168             return valid;
33169         }
33170         
33171         this.markInvalid();
33172         
33173         return valid;
33174     },
33175     
33176     markValid : function()
33177     {
33178         
33179         var label = this.el.select('label', true).first();
33180         var icon = this.el.select('i.fa-star', true).first();
33181
33182         if(label && icon){
33183             icon.remove();
33184         }
33185         
33186         this.fireEvent('valid', this);
33187     },
33188     
33189      /**
33190      * Mark this field as invalid
33191      * @param {String} msg The validation message
33192      */
33193     markInvalid : function(msg)
33194     {
33195         
33196         var label = this.el.select('label', true).first();
33197         var icon = this.el.select('i.fa-star', true).first();
33198
33199         if(label && !icon){
33200             this.el.select('.roo-date-split-field-label', true).createChild({
33201                 tag : 'i',
33202                 cls : 'text-danger fa fa-lg fa-star',
33203                 tooltip : 'This field is required',
33204                 style : 'margin-right:5px;'
33205             }, label, true);
33206         }
33207         
33208         this.fireEvent('invalid', this, msg);
33209     },
33210     
33211     clearInvalid : function()
33212     {
33213         var label = this.el.select('label', true).first();
33214         var icon = this.el.select('i.fa-star', true).first();
33215
33216         if(label && icon){
33217             icon.remove();
33218         }
33219         
33220         this.fireEvent('valid', this);
33221     },
33222     
33223     getName: function()
33224     {
33225         return this.name;
33226     }
33227     
33228 });
33229
33230  /**
33231  *
33232  * This is based on 
33233  * http://masonry.desandro.com
33234  *
33235  * The idea is to render all the bricks based on vertical width...
33236  *
33237  * The original code extends 'outlayer' - we might need to use that....
33238  * 
33239  */
33240
33241
33242 /**
33243  * @class Roo.bootstrap.LayoutMasonry
33244  * @extends Roo.bootstrap.Component
33245  * Bootstrap Layout Masonry class
33246  * 
33247  * @constructor
33248  * Create a new Element
33249  * @param {Object} config The config object
33250  */
33251
33252 Roo.bootstrap.LayoutMasonry = function(config){
33253     
33254     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33255     
33256     this.bricks = [];
33257     
33258     Roo.bootstrap.LayoutMasonry.register(this);
33259     
33260     this.addEvents({
33261         // raw events
33262         /**
33263          * @event layout
33264          * Fire after layout the items
33265          * @param {Roo.bootstrap.LayoutMasonry} this
33266          * @param {Roo.EventObject} e
33267          */
33268         "layout" : true
33269     });
33270     
33271 };
33272
33273 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33274     
33275     /**
33276      * @cfg {Boolean} isLayoutInstant = no animation?
33277      */   
33278     isLayoutInstant : false, // needed?
33279    
33280     /**
33281      * @cfg {Number} boxWidth  width of the columns
33282      */   
33283     boxWidth : 450,
33284     
33285       /**
33286      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33287      */   
33288     boxHeight : 0,
33289     
33290     /**
33291      * @cfg {Number} padWidth padding below box..
33292      */   
33293     padWidth : 10, 
33294     
33295     /**
33296      * @cfg {Number} gutter gutter width..
33297      */   
33298     gutter : 10,
33299     
33300      /**
33301      * @cfg {Number} maxCols maximum number of columns
33302      */   
33303     
33304     maxCols: 0,
33305     
33306     /**
33307      * @cfg {Boolean} isAutoInitial defalut true
33308      */   
33309     isAutoInitial : true, 
33310     
33311     containerWidth: 0,
33312     
33313     /**
33314      * @cfg {Boolean} isHorizontal defalut false
33315      */   
33316     isHorizontal : false, 
33317
33318     currentSize : null,
33319     
33320     tag: 'div',
33321     
33322     cls: '',
33323     
33324     bricks: null, //CompositeElement
33325     
33326     cols : 1,
33327     
33328     _isLayoutInited : false,
33329     
33330 //    isAlternative : false, // only use for vertical layout...
33331     
33332     /**
33333      * @cfg {Number} alternativePadWidth padding below box..
33334      */   
33335     alternativePadWidth : 50,
33336     
33337     selectedBrick : [],
33338     
33339     getAutoCreate : function(){
33340         
33341         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33342         
33343         var cfg = {
33344             tag: this.tag,
33345             cls: 'blog-masonary-wrapper ' + this.cls,
33346             cn : {
33347                 cls : 'mas-boxes masonary'
33348             }
33349         };
33350         
33351         return cfg;
33352     },
33353     
33354     getChildContainer: function( )
33355     {
33356         if (this.boxesEl) {
33357             return this.boxesEl;
33358         }
33359         
33360         this.boxesEl = this.el.select('.mas-boxes').first();
33361         
33362         return this.boxesEl;
33363     },
33364     
33365     
33366     initEvents : function()
33367     {
33368         var _this = this;
33369         
33370         if(this.isAutoInitial){
33371             Roo.log('hook children rendered');
33372             this.on('childrenrendered', function() {
33373                 Roo.log('children rendered');
33374                 _this.initial();
33375             } ,this);
33376         }
33377     },
33378     
33379     initial : function()
33380     {
33381         this.selectedBrick = [];
33382         
33383         this.currentSize = this.el.getBox(true);
33384         
33385         Roo.EventManager.onWindowResize(this.resize, this); 
33386
33387         if(!this.isAutoInitial){
33388             this.layout();
33389             return;
33390         }
33391         
33392         this.layout();
33393         
33394         return;
33395         //this.layout.defer(500,this);
33396         
33397     },
33398     
33399     resize : function()
33400     {
33401         var cs = this.el.getBox(true);
33402         
33403         if (
33404                 this.currentSize.width == cs.width && 
33405                 this.currentSize.x == cs.x && 
33406                 this.currentSize.height == cs.height && 
33407                 this.currentSize.y == cs.y 
33408         ) {
33409             Roo.log("no change in with or X or Y");
33410             return;
33411         }
33412         
33413         this.currentSize = cs;
33414         
33415         this.layout();
33416         
33417     },
33418     
33419     layout : function()
33420     {   
33421         this._resetLayout();
33422         
33423         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33424         
33425         this.layoutItems( isInstant );
33426       
33427         this._isLayoutInited = true;
33428         
33429         this.fireEvent('layout', this);
33430         
33431     },
33432     
33433     _resetLayout : function()
33434     {
33435         if(this.isHorizontal){
33436             this.horizontalMeasureColumns();
33437             return;
33438         }
33439         
33440         this.verticalMeasureColumns();
33441         
33442     },
33443     
33444     verticalMeasureColumns : function()
33445     {
33446         this.getContainerWidth();
33447         
33448 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33449 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33450 //            return;
33451 //        }
33452         
33453         var boxWidth = this.boxWidth + this.padWidth;
33454         
33455         if(this.containerWidth < this.boxWidth){
33456             boxWidth = this.containerWidth
33457         }
33458         
33459         var containerWidth = this.containerWidth;
33460         
33461         var cols = Math.floor(containerWidth / boxWidth);
33462         
33463         this.cols = Math.max( cols, 1 );
33464         
33465         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33466         
33467         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33468         
33469         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33470         
33471         this.colWidth = boxWidth + avail - this.padWidth;
33472         
33473         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33474         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33475     },
33476     
33477     horizontalMeasureColumns : function()
33478     {
33479         this.getContainerWidth();
33480         
33481         var boxWidth = this.boxWidth;
33482         
33483         if(this.containerWidth < boxWidth){
33484             boxWidth = this.containerWidth;
33485         }
33486         
33487         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33488         
33489         this.el.setHeight(boxWidth);
33490         
33491     },
33492     
33493     getContainerWidth : function()
33494     {
33495         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33496     },
33497     
33498     layoutItems : function( isInstant )
33499     {
33500         Roo.log(this.bricks);
33501         
33502         var items = Roo.apply([], this.bricks);
33503         
33504         if(this.isHorizontal){
33505             this._horizontalLayoutItems( items , isInstant );
33506             return;
33507         }
33508         
33509 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33510 //            this._verticalAlternativeLayoutItems( items , isInstant );
33511 //            return;
33512 //        }
33513         
33514         this._verticalLayoutItems( items , isInstant );
33515         
33516     },
33517     
33518     _verticalLayoutItems : function ( items , isInstant)
33519     {
33520         if ( !items || !items.length ) {
33521             return;
33522         }
33523         
33524         var standard = [
33525             ['xs', 'xs', 'xs', 'tall'],
33526             ['xs', 'xs', 'tall'],
33527             ['xs', 'xs', 'sm'],
33528             ['xs', 'xs', 'xs'],
33529             ['xs', 'tall'],
33530             ['xs', 'sm'],
33531             ['xs', 'xs'],
33532             ['xs'],
33533             
33534             ['sm', 'xs', 'xs'],
33535             ['sm', 'xs'],
33536             ['sm'],
33537             
33538             ['tall', 'xs', 'xs', 'xs'],
33539             ['tall', 'xs', 'xs'],
33540             ['tall', 'xs'],
33541             ['tall']
33542             
33543         ];
33544         
33545         var queue = [];
33546         
33547         var boxes = [];
33548         
33549         var box = [];
33550         
33551         Roo.each(items, function(item, k){
33552             
33553             switch (item.size) {
33554                 // these layouts take up a full box,
33555                 case 'md' :
33556                 case 'md-left' :
33557                 case 'md-right' :
33558                 case 'wide' :
33559                     
33560                     if(box.length){
33561                         boxes.push(box);
33562                         box = [];
33563                     }
33564                     
33565                     boxes.push([item]);
33566                     
33567                     break;
33568                     
33569                 case 'xs' :
33570                 case 'sm' :
33571                 case 'tall' :
33572                     
33573                     box.push(item);
33574                     
33575                     break;
33576                 default :
33577                     break;
33578                     
33579             }
33580             
33581         }, this);
33582         
33583         if(box.length){
33584             boxes.push(box);
33585             box = [];
33586         }
33587         
33588         var filterPattern = function(box, length)
33589         {
33590             if(!box.length){
33591                 return;
33592             }
33593             
33594             var match = false;
33595             
33596             var pattern = box.slice(0, length);
33597             
33598             var format = [];
33599             
33600             Roo.each(pattern, function(i){
33601                 format.push(i.size);
33602             }, this);
33603             
33604             Roo.each(standard, function(s){
33605                 
33606                 if(String(s) != String(format)){
33607                     return;
33608                 }
33609                 
33610                 match = true;
33611                 return false;
33612                 
33613             }, this);
33614             
33615             if(!match && length == 1){
33616                 return;
33617             }
33618             
33619             if(!match){
33620                 filterPattern(box, length - 1);
33621                 return;
33622             }
33623                 
33624             queue.push(pattern);
33625
33626             box = box.slice(length, box.length);
33627
33628             filterPattern(box, 4);
33629
33630             return;
33631             
33632         }
33633         
33634         Roo.each(boxes, function(box, k){
33635             
33636             if(!box.length){
33637                 return;
33638             }
33639             
33640             if(box.length == 1){
33641                 queue.push(box);
33642                 return;
33643             }
33644             
33645             filterPattern(box, 4);
33646             
33647         }, this);
33648         
33649         this._processVerticalLayoutQueue( queue, isInstant );
33650         
33651     },
33652     
33653 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33654 //    {
33655 //        if ( !items || !items.length ) {
33656 //            return;
33657 //        }
33658 //
33659 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33660 //        
33661 //    },
33662     
33663     _horizontalLayoutItems : function ( items , isInstant)
33664     {
33665         if ( !items || !items.length || items.length < 3) {
33666             return;
33667         }
33668         
33669         items.reverse();
33670         
33671         var eItems = items.slice(0, 3);
33672         
33673         items = items.slice(3, items.length);
33674         
33675         var standard = [
33676             ['xs', 'xs', 'xs', 'wide'],
33677             ['xs', 'xs', 'wide'],
33678             ['xs', 'xs', 'sm'],
33679             ['xs', 'xs', 'xs'],
33680             ['xs', 'wide'],
33681             ['xs', 'sm'],
33682             ['xs', 'xs'],
33683             ['xs'],
33684             
33685             ['sm', 'xs', 'xs'],
33686             ['sm', 'xs'],
33687             ['sm'],
33688             
33689             ['wide', 'xs', 'xs', 'xs'],
33690             ['wide', 'xs', 'xs'],
33691             ['wide', 'xs'],
33692             ['wide'],
33693             
33694             ['wide-thin']
33695         ];
33696         
33697         var queue = [];
33698         
33699         var boxes = [];
33700         
33701         var box = [];
33702         
33703         Roo.each(items, function(item, k){
33704             
33705             switch (item.size) {
33706                 case 'md' :
33707                 case 'md-left' :
33708                 case 'md-right' :
33709                 case 'tall' :
33710                     
33711                     if(box.length){
33712                         boxes.push(box);
33713                         box = [];
33714                     }
33715                     
33716                     boxes.push([item]);
33717                     
33718                     break;
33719                     
33720                 case 'xs' :
33721                 case 'sm' :
33722                 case 'wide' :
33723                 case 'wide-thin' :
33724                     
33725                     box.push(item);
33726                     
33727                     break;
33728                 default :
33729                     break;
33730                     
33731             }
33732             
33733         }, this);
33734         
33735         if(box.length){
33736             boxes.push(box);
33737             box = [];
33738         }
33739         
33740         var filterPattern = function(box, length)
33741         {
33742             if(!box.length){
33743                 return;
33744             }
33745             
33746             var match = false;
33747             
33748             var pattern = box.slice(0, length);
33749             
33750             var format = [];
33751             
33752             Roo.each(pattern, function(i){
33753                 format.push(i.size);
33754             }, this);
33755             
33756             Roo.each(standard, function(s){
33757                 
33758                 if(String(s) != String(format)){
33759                     return;
33760                 }
33761                 
33762                 match = true;
33763                 return false;
33764                 
33765             }, this);
33766             
33767             if(!match && length == 1){
33768                 return;
33769             }
33770             
33771             if(!match){
33772                 filterPattern(box, length - 1);
33773                 return;
33774             }
33775                 
33776             queue.push(pattern);
33777
33778             box = box.slice(length, box.length);
33779
33780             filterPattern(box, 4);
33781
33782             return;
33783             
33784         }
33785         
33786         Roo.each(boxes, function(box, k){
33787             
33788             if(!box.length){
33789                 return;
33790             }
33791             
33792             if(box.length == 1){
33793                 queue.push(box);
33794                 return;
33795             }
33796             
33797             filterPattern(box, 4);
33798             
33799         }, this);
33800         
33801         
33802         var prune = [];
33803         
33804         var pos = this.el.getBox(true);
33805         
33806         var minX = pos.x;
33807         
33808         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33809         
33810         var hit_end = false;
33811         
33812         Roo.each(queue, function(box){
33813             
33814             if(hit_end){
33815                 
33816                 Roo.each(box, function(b){
33817                 
33818                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33819                     b.el.hide();
33820
33821                 }, this);
33822
33823                 return;
33824             }
33825             
33826             var mx = 0;
33827             
33828             Roo.each(box, function(b){
33829                 
33830                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33831                 b.el.show();
33832
33833                 mx = Math.max(mx, b.x);
33834                 
33835             }, this);
33836             
33837             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33838             
33839             if(maxX < minX){
33840                 
33841                 Roo.each(box, function(b){
33842                 
33843                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33844                     b.el.hide();
33845                     
33846                 }, this);
33847                 
33848                 hit_end = true;
33849                 
33850                 return;
33851             }
33852             
33853             prune.push(box);
33854             
33855         }, this);
33856         
33857         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33858     },
33859     
33860     /** Sets position of item in DOM
33861     * @param {Element} item
33862     * @param {Number} x - horizontal position
33863     * @param {Number} y - vertical position
33864     * @param {Boolean} isInstant - disables transitions
33865     */
33866     _processVerticalLayoutQueue : function( queue, isInstant )
33867     {
33868         var pos = this.el.getBox(true);
33869         var x = pos.x;
33870         var y = pos.y;
33871         var maxY = [];
33872         
33873         for (var i = 0; i < this.cols; i++){
33874             maxY[i] = pos.y;
33875         }
33876         
33877         Roo.each(queue, function(box, k){
33878             
33879             var col = k % this.cols;
33880             
33881             Roo.each(box, function(b,kk){
33882                 
33883                 b.el.position('absolute');
33884                 
33885                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33886                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33887                 
33888                 if(b.size == 'md-left' || b.size == 'md-right'){
33889                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33890                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33891                 }
33892                 
33893                 b.el.setWidth(width);
33894                 b.el.setHeight(height);
33895                 // iframe?
33896                 b.el.select('iframe',true).setSize(width,height);
33897                 
33898             }, this);
33899             
33900             for (var i = 0; i < this.cols; i++){
33901                 
33902                 if(maxY[i] < maxY[col]){
33903                     col = i;
33904                     continue;
33905                 }
33906                 
33907                 col = Math.min(col, i);
33908                 
33909             }
33910             
33911             x = pos.x + col * (this.colWidth + this.padWidth);
33912             
33913             y = maxY[col];
33914             
33915             var positions = [];
33916             
33917             switch (box.length){
33918                 case 1 :
33919                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33920                     break;
33921                 case 2 :
33922                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33923                     break;
33924                 case 3 :
33925                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33926                     break;
33927                 case 4 :
33928                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33929                     break;
33930                 default :
33931                     break;
33932             }
33933             
33934             Roo.each(box, function(b,kk){
33935                 
33936                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33937                 
33938                 var sz = b.el.getSize();
33939                 
33940                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33941                 
33942             }, this);
33943             
33944         }, this);
33945         
33946         var mY = 0;
33947         
33948         for (var i = 0; i < this.cols; i++){
33949             mY = Math.max(mY, maxY[i]);
33950         }
33951         
33952         this.el.setHeight(mY - pos.y);
33953         
33954     },
33955     
33956 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33957 //    {
33958 //        var pos = this.el.getBox(true);
33959 //        var x = pos.x;
33960 //        var y = pos.y;
33961 //        var maxX = pos.right;
33962 //        
33963 //        var maxHeight = 0;
33964 //        
33965 //        Roo.each(items, function(item, k){
33966 //            
33967 //            var c = k % 2;
33968 //            
33969 //            item.el.position('absolute');
33970 //                
33971 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33972 //
33973 //            item.el.setWidth(width);
33974 //
33975 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33976 //
33977 //            item.el.setHeight(height);
33978 //            
33979 //            if(c == 0){
33980 //                item.el.setXY([x, y], isInstant ? false : true);
33981 //            } else {
33982 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33983 //            }
33984 //            
33985 //            y = y + height + this.alternativePadWidth;
33986 //            
33987 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33988 //            
33989 //        }, this);
33990 //        
33991 //        this.el.setHeight(maxHeight);
33992 //        
33993 //    },
33994     
33995     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33996     {
33997         var pos = this.el.getBox(true);
33998         
33999         var minX = pos.x;
34000         var minY = pos.y;
34001         
34002         var maxX = pos.right;
34003         
34004         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34005         
34006         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34007         
34008         Roo.each(queue, function(box, k){
34009             
34010             Roo.each(box, function(b, kk){
34011                 
34012                 b.el.position('absolute');
34013                 
34014                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34015                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34016                 
34017                 if(b.size == 'md-left' || b.size == 'md-right'){
34018                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34019                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34020                 }
34021                 
34022                 b.el.setWidth(width);
34023                 b.el.setHeight(height);
34024                 
34025             }, this);
34026             
34027             if(!box.length){
34028                 return;
34029             }
34030             
34031             var positions = [];
34032             
34033             switch (box.length){
34034                 case 1 :
34035                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34036                     break;
34037                 case 2 :
34038                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34039                     break;
34040                 case 3 :
34041                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34042                     break;
34043                 case 4 :
34044                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34045                     break;
34046                 default :
34047                     break;
34048             }
34049             
34050             Roo.each(box, function(b,kk){
34051                 
34052                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34053                 
34054                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34055                 
34056             }, this);
34057             
34058         }, this);
34059         
34060     },
34061     
34062     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34063     {
34064         Roo.each(eItems, function(b,k){
34065             
34066             b.size = (k == 0) ? 'sm' : 'xs';
34067             b.x = (k == 0) ? 2 : 1;
34068             b.y = (k == 0) ? 2 : 1;
34069             
34070             b.el.position('absolute');
34071             
34072             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34073                 
34074             b.el.setWidth(width);
34075             
34076             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34077             
34078             b.el.setHeight(height);
34079             
34080         }, this);
34081
34082         var positions = [];
34083         
34084         positions.push({
34085             x : maxX - this.unitWidth * 2 - this.gutter,
34086             y : minY
34087         });
34088         
34089         positions.push({
34090             x : maxX - this.unitWidth,
34091             y : minY + (this.unitWidth + this.gutter) * 2
34092         });
34093         
34094         positions.push({
34095             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34096             y : minY
34097         });
34098         
34099         Roo.each(eItems, function(b,k){
34100             
34101             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34102
34103         }, this);
34104         
34105     },
34106     
34107     getVerticalOneBoxColPositions : function(x, y, box)
34108     {
34109         var pos = [];
34110         
34111         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34112         
34113         if(box[0].size == 'md-left'){
34114             rand = 0;
34115         }
34116         
34117         if(box[0].size == 'md-right'){
34118             rand = 1;
34119         }
34120         
34121         pos.push({
34122             x : x + (this.unitWidth + this.gutter) * rand,
34123             y : y
34124         });
34125         
34126         return pos;
34127     },
34128     
34129     getVerticalTwoBoxColPositions : function(x, y, box)
34130     {
34131         var pos = [];
34132         
34133         if(box[0].size == 'xs'){
34134             
34135             pos.push({
34136                 x : x,
34137                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34138             });
34139
34140             pos.push({
34141                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34142                 y : y
34143             });
34144             
34145             return pos;
34146             
34147         }
34148         
34149         pos.push({
34150             x : x,
34151             y : y
34152         });
34153
34154         pos.push({
34155             x : x + (this.unitWidth + this.gutter) * 2,
34156             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34157         });
34158         
34159         return pos;
34160         
34161     },
34162     
34163     getVerticalThreeBoxColPositions : function(x, y, box)
34164     {
34165         var pos = [];
34166         
34167         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34168             
34169             pos.push({
34170                 x : x,
34171                 y : y
34172             });
34173
34174             pos.push({
34175                 x : x + (this.unitWidth + this.gutter) * 1,
34176                 y : y
34177             });
34178             
34179             pos.push({
34180                 x : x + (this.unitWidth + this.gutter) * 2,
34181                 y : y
34182             });
34183             
34184             return pos;
34185             
34186         }
34187         
34188         if(box[0].size == 'xs' && box[1].size == 'xs'){
34189             
34190             pos.push({
34191                 x : x,
34192                 y : y
34193             });
34194
34195             pos.push({
34196                 x : x,
34197                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34198             });
34199             
34200             pos.push({
34201                 x : x + (this.unitWidth + this.gutter) * 1,
34202                 y : y
34203             });
34204             
34205             return pos;
34206             
34207         }
34208         
34209         pos.push({
34210             x : x,
34211             y : y
34212         });
34213
34214         pos.push({
34215             x : x + (this.unitWidth + this.gutter) * 2,
34216             y : y
34217         });
34218
34219         pos.push({
34220             x : x + (this.unitWidth + this.gutter) * 2,
34221             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34222         });
34223             
34224         return pos;
34225         
34226     },
34227     
34228     getVerticalFourBoxColPositions : function(x, y, box)
34229     {
34230         var pos = [];
34231         
34232         if(box[0].size == 'xs'){
34233             
34234             pos.push({
34235                 x : x,
34236                 y : y
34237             });
34238
34239             pos.push({
34240                 x : x,
34241                 y : y + (this.unitHeight + this.gutter) * 1
34242             });
34243             
34244             pos.push({
34245                 x : x,
34246                 y : y + (this.unitHeight + this.gutter) * 2
34247             });
34248             
34249             pos.push({
34250                 x : x + (this.unitWidth + this.gutter) * 1,
34251                 y : y
34252             });
34253             
34254             return pos;
34255             
34256         }
34257         
34258         pos.push({
34259             x : x,
34260             y : y
34261         });
34262
34263         pos.push({
34264             x : x + (this.unitWidth + this.gutter) * 2,
34265             y : y
34266         });
34267
34268         pos.push({
34269             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34270             y : y + (this.unitHeight + this.gutter) * 1
34271         });
34272
34273         pos.push({
34274             x : x + (this.unitWidth + this.gutter) * 2,
34275             y : y + (this.unitWidth + this.gutter) * 2
34276         });
34277
34278         return pos;
34279         
34280     },
34281     
34282     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34283     {
34284         var pos = [];
34285         
34286         if(box[0].size == 'md-left'){
34287             pos.push({
34288                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34289                 y : minY
34290             });
34291             
34292             return pos;
34293         }
34294         
34295         if(box[0].size == 'md-right'){
34296             pos.push({
34297                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34298                 y : minY + (this.unitWidth + this.gutter) * 1
34299             });
34300             
34301             return pos;
34302         }
34303         
34304         var rand = Math.floor(Math.random() * (4 - box[0].y));
34305         
34306         pos.push({
34307             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34308             y : minY + (this.unitWidth + this.gutter) * rand
34309         });
34310         
34311         return pos;
34312         
34313     },
34314     
34315     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34316     {
34317         var pos = [];
34318         
34319         if(box[0].size == 'xs'){
34320             
34321             pos.push({
34322                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34323                 y : minY
34324             });
34325
34326             pos.push({
34327                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34328                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34329             });
34330             
34331             return pos;
34332             
34333         }
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) * 2
34343         });
34344         
34345         return pos;
34346         
34347     },
34348     
34349     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34350     {
34351         var pos = [];
34352         
34353         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34354             
34355             pos.push({
34356                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34357                 y : minY
34358             });
34359
34360             pos.push({
34361                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34362                 y : minY + (this.unitWidth + this.gutter) * 1
34363             });
34364             
34365             pos.push({
34366                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34367                 y : minY + (this.unitWidth + this.gutter) * 2
34368             });
34369             
34370             return pos;
34371             
34372         }
34373         
34374         if(box[0].size == 'xs' && box[1].size == 'xs'){
34375             
34376             pos.push({
34377                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34378                 y : minY
34379             });
34380
34381             pos.push({
34382                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34383                 y : minY
34384             });
34385             
34386             pos.push({
34387                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34388                 y : minY + (this.unitWidth + this.gutter) * 1
34389             });
34390             
34391             return pos;
34392             
34393         }
34394         
34395         pos.push({
34396             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34397             y : minY
34398         });
34399
34400         pos.push({
34401             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34402             y : minY + (this.unitWidth + this.gutter) * 2
34403         });
34404
34405         pos.push({
34406             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34407             y : minY + (this.unitWidth + this.gutter) * 2
34408         });
34409             
34410         return pos;
34411         
34412     },
34413     
34414     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34415     {
34416         var pos = [];
34417         
34418         if(box[0].size == 'xs'){
34419             
34420             pos.push({
34421                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34422                 y : minY
34423             });
34424
34425             pos.push({
34426                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34427                 y : minY
34428             });
34429             
34430             pos.push({
34431                 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),
34432                 y : minY
34433             });
34434             
34435             pos.push({
34436                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34437                 y : minY + (this.unitWidth + this.gutter) * 1
34438             });
34439             
34440             return pos;
34441             
34442         }
34443         
34444         pos.push({
34445             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34446             y : minY
34447         });
34448         
34449         pos.push({
34450             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34451             y : minY + (this.unitWidth + this.gutter) * 2
34452         });
34453         
34454         pos.push({
34455             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34456             y : minY + (this.unitWidth + this.gutter) * 2
34457         });
34458         
34459         pos.push({
34460             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),
34461             y : minY + (this.unitWidth + this.gutter) * 2
34462         });
34463
34464         return pos;
34465         
34466     },
34467     
34468     /**
34469     * remove a Masonry Brick
34470     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34471     */
34472     removeBrick : function(brick_id)
34473     {
34474         if (!brick_id) {
34475             return;
34476         }
34477         
34478         for (var i = 0; i<this.bricks.length; i++) {
34479             if (this.bricks[i].id == brick_id) {
34480                 this.bricks.splice(i,1);
34481                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34482                 this.initial();
34483             }
34484         }
34485     },
34486     
34487     /**
34488     * adds a Masonry Brick
34489     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34490     */
34491     addBrick : function(cfg)
34492     {
34493         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34494         //this.register(cn);
34495         cn.parentId = this.id;
34496         cn.render(this.el);
34497         return cn;
34498     },
34499     
34500     /**
34501     * register a Masonry Brick
34502     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34503     */
34504     
34505     register : function(brick)
34506     {
34507         this.bricks.push(brick);
34508         brick.masonryId = this.id;
34509     },
34510     
34511     /**
34512     * clear all the Masonry Brick
34513     */
34514     clearAll : function()
34515     {
34516         this.bricks = [];
34517         //this.getChildContainer().dom.innerHTML = "";
34518         this.el.dom.innerHTML = '';
34519     },
34520     
34521     getSelected : function()
34522     {
34523         if (!this.selectedBrick) {
34524             return false;
34525         }
34526         
34527         return this.selectedBrick;
34528     }
34529 });
34530
34531 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34532     
34533     groups: {},
34534      /**
34535     * register a Masonry Layout
34536     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34537     */
34538     
34539     register : function(layout)
34540     {
34541         this.groups[layout.id] = layout;
34542     },
34543     /**
34544     * fetch a  Masonry Layout based on the masonry layout ID
34545     * @param {string} the masonry layout to add
34546     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34547     */
34548     
34549     get: function(layout_id) {
34550         if (typeof(this.groups[layout_id]) == 'undefined') {
34551             return false;
34552         }
34553         return this.groups[layout_id] ;
34554     }
34555     
34556     
34557     
34558 });
34559
34560  
34561
34562  /**
34563  *
34564  * This is based on 
34565  * http://masonry.desandro.com
34566  *
34567  * The idea is to render all the bricks based on vertical width...
34568  *
34569  * The original code extends 'outlayer' - we might need to use that....
34570  * 
34571  */
34572
34573
34574 /**
34575  * @class Roo.bootstrap.LayoutMasonryAuto
34576  * @extends Roo.bootstrap.Component
34577  * Bootstrap Layout Masonry class
34578  * 
34579  * @constructor
34580  * Create a new Element
34581  * @param {Object} config The config object
34582  */
34583
34584 Roo.bootstrap.LayoutMasonryAuto = function(config){
34585     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34586 };
34587
34588 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34589     
34590       /**
34591      * @cfg {Boolean} isFitWidth  - resize the width..
34592      */   
34593     isFitWidth : false,  // options..
34594     /**
34595      * @cfg {Boolean} isOriginLeft = left align?
34596      */   
34597     isOriginLeft : true,
34598     /**
34599      * @cfg {Boolean} isOriginTop = top align?
34600      */   
34601     isOriginTop : false,
34602     /**
34603      * @cfg {Boolean} isLayoutInstant = no animation?
34604      */   
34605     isLayoutInstant : false, // needed?
34606     /**
34607      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34608      */   
34609     isResizingContainer : true,
34610     /**
34611      * @cfg {Number} columnWidth  width of the columns 
34612      */   
34613     
34614     columnWidth : 0,
34615     
34616     /**
34617      * @cfg {Number} maxCols maximum number of columns
34618      */   
34619     
34620     maxCols: 0,
34621     /**
34622      * @cfg {Number} padHeight padding below box..
34623      */   
34624     
34625     padHeight : 10, 
34626     
34627     /**
34628      * @cfg {Boolean} isAutoInitial defalut true
34629      */   
34630     
34631     isAutoInitial : true, 
34632     
34633     // private?
34634     gutter : 0,
34635     
34636     containerWidth: 0,
34637     initialColumnWidth : 0,
34638     currentSize : null,
34639     
34640     colYs : null, // array.
34641     maxY : 0,
34642     padWidth: 10,
34643     
34644     
34645     tag: 'div',
34646     cls: '',
34647     bricks: null, //CompositeElement
34648     cols : 0, // array?
34649     // element : null, // wrapped now this.el
34650     _isLayoutInited : null, 
34651     
34652     
34653     getAutoCreate : function(){
34654         
34655         var cfg = {
34656             tag: this.tag,
34657             cls: 'blog-masonary-wrapper ' + this.cls,
34658             cn : {
34659                 cls : 'mas-boxes masonary'
34660             }
34661         };
34662         
34663         return cfg;
34664     },
34665     
34666     getChildContainer: function( )
34667     {
34668         if (this.boxesEl) {
34669             return this.boxesEl;
34670         }
34671         
34672         this.boxesEl = this.el.select('.mas-boxes').first();
34673         
34674         return this.boxesEl;
34675     },
34676     
34677     
34678     initEvents : function()
34679     {
34680         var _this = this;
34681         
34682         if(this.isAutoInitial){
34683             Roo.log('hook children rendered');
34684             this.on('childrenrendered', function() {
34685                 Roo.log('children rendered');
34686                 _this.initial();
34687             } ,this);
34688         }
34689         
34690     },
34691     
34692     initial : function()
34693     {
34694         this.reloadItems();
34695
34696         this.currentSize = this.el.getBox(true);
34697
34698         /// was window resize... - let's see if this works..
34699         Roo.EventManager.onWindowResize(this.resize, this); 
34700
34701         if(!this.isAutoInitial){
34702             this.layout();
34703             return;
34704         }
34705         
34706         this.layout.defer(500,this);
34707     },
34708     
34709     reloadItems: function()
34710     {
34711         this.bricks = this.el.select('.masonry-brick', true);
34712         
34713         this.bricks.each(function(b) {
34714             //Roo.log(b.getSize());
34715             if (!b.attr('originalwidth')) {
34716                 b.attr('originalwidth',  b.getSize().width);
34717             }
34718             
34719         });
34720         
34721         Roo.log(this.bricks.elements.length);
34722     },
34723     
34724     resize : function()
34725     {
34726         Roo.log('resize');
34727         var cs = this.el.getBox(true);
34728         
34729         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34730             Roo.log("no change in with or X");
34731             return;
34732         }
34733         this.currentSize = cs;
34734         this.layout();
34735     },
34736     
34737     layout : function()
34738     {
34739          Roo.log('layout');
34740         this._resetLayout();
34741         //this._manageStamps();
34742       
34743         // don't animate first layout
34744         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34745         this.layoutItems( isInstant );
34746       
34747         // flag for initalized
34748         this._isLayoutInited = true;
34749     },
34750     
34751     layoutItems : function( isInstant )
34752     {
34753         //var items = this._getItemsForLayout( this.items );
34754         // original code supports filtering layout items.. we just ignore it..
34755         
34756         this._layoutItems( this.bricks , isInstant );
34757       
34758         this._postLayout();
34759     },
34760     _layoutItems : function ( items , isInstant)
34761     {
34762        //this.fireEvent( 'layout', this, items );
34763     
34764
34765         if ( !items || !items.elements.length ) {
34766           // no items, emit event with empty array
34767             return;
34768         }
34769
34770         var queue = [];
34771         items.each(function(item) {
34772             Roo.log("layout item");
34773             Roo.log(item);
34774             // get x/y object from method
34775             var position = this._getItemLayoutPosition( item );
34776             // enqueue
34777             position.item = item;
34778             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34779             queue.push( position );
34780         }, this);
34781       
34782         this._processLayoutQueue( queue );
34783     },
34784     /** Sets position of item in DOM
34785     * @param {Element} item
34786     * @param {Number} x - horizontal position
34787     * @param {Number} y - vertical position
34788     * @param {Boolean} isInstant - disables transitions
34789     */
34790     _processLayoutQueue : function( queue )
34791     {
34792         for ( var i=0, len = queue.length; i < len; i++ ) {
34793             var obj = queue[i];
34794             obj.item.position('absolute');
34795             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34796         }
34797     },
34798       
34799     
34800     /**
34801     * Any logic you want to do after each layout,
34802     * i.e. size the container
34803     */
34804     _postLayout : function()
34805     {
34806         this.resizeContainer();
34807     },
34808     
34809     resizeContainer : function()
34810     {
34811         if ( !this.isResizingContainer ) {
34812             return;
34813         }
34814         var size = this._getContainerSize();
34815         if ( size ) {
34816             this.el.setSize(size.width,size.height);
34817             this.boxesEl.setSize(size.width,size.height);
34818         }
34819     },
34820     
34821     
34822     
34823     _resetLayout : function()
34824     {
34825         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34826         this.colWidth = this.el.getWidth();
34827         //this.gutter = this.el.getWidth(); 
34828         
34829         this.measureColumns();
34830
34831         // reset column Y
34832         var i = this.cols;
34833         this.colYs = [];
34834         while (i--) {
34835             this.colYs.push( 0 );
34836         }
34837     
34838         this.maxY = 0;
34839     },
34840
34841     measureColumns : function()
34842     {
34843         this.getContainerWidth();
34844       // if columnWidth is 0, default to outerWidth of first item
34845         if ( !this.columnWidth ) {
34846             var firstItem = this.bricks.first();
34847             Roo.log(firstItem);
34848             this.columnWidth  = this.containerWidth;
34849             if (firstItem && firstItem.attr('originalwidth') ) {
34850                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34851             }
34852             // columnWidth fall back to item of first element
34853             Roo.log("set column width?");
34854                         this.initialColumnWidth = this.columnWidth  ;
34855
34856             // if first elem has no width, default to size of container
34857             
34858         }
34859         
34860         
34861         if (this.initialColumnWidth) {
34862             this.columnWidth = this.initialColumnWidth;
34863         }
34864         
34865         
34866             
34867         // column width is fixed at the top - however if container width get's smaller we should
34868         // reduce it...
34869         
34870         // this bit calcs how man columns..
34871             
34872         var columnWidth = this.columnWidth += this.gutter;
34873       
34874         // calculate columns
34875         var containerWidth = this.containerWidth + this.gutter;
34876         
34877         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34878         // fix rounding errors, typically with gutters
34879         var excess = columnWidth - containerWidth % columnWidth;
34880         
34881         
34882         // if overshoot is less than a pixel, round up, otherwise floor it
34883         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34884         cols = Math[ mathMethod ]( cols );
34885         this.cols = Math.max( cols, 1 );
34886         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34887         
34888          // padding positioning..
34889         var totalColWidth = this.cols * this.columnWidth;
34890         var padavail = this.containerWidth - totalColWidth;
34891         // so for 2 columns - we need 3 'pads'
34892         
34893         var padNeeded = (1+this.cols) * this.padWidth;
34894         
34895         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34896         
34897         this.columnWidth += padExtra
34898         //this.padWidth = Math.floor(padavail /  ( this.cols));
34899         
34900         // adjust colum width so that padding is fixed??
34901         
34902         // we have 3 columns ... total = width * 3
34903         // we have X left over... that should be used by 
34904         
34905         //if (this.expandC) {
34906             
34907         //}
34908         
34909         
34910         
34911     },
34912     
34913     getContainerWidth : function()
34914     {
34915        /* // container is parent if fit width
34916         var container = this.isFitWidth ? this.element.parentNode : this.element;
34917         // check that this.size and size are there
34918         // IE8 triggers resize on body size change, so they might not be
34919         
34920         var size = getSize( container );  //FIXME
34921         this.containerWidth = size && size.innerWidth; //FIXME
34922         */
34923          
34924         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34925         
34926     },
34927     
34928     _getItemLayoutPosition : function( item )  // what is item?
34929     {
34930         // we resize the item to our columnWidth..
34931       
34932         item.setWidth(this.columnWidth);
34933         item.autoBoxAdjust  = false;
34934         
34935         var sz = item.getSize();
34936  
34937         // how many columns does this brick span
34938         var remainder = this.containerWidth % this.columnWidth;
34939         
34940         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34941         // round if off by 1 pixel, otherwise use ceil
34942         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34943         colSpan = Math.min( colSpan, this.cols );
34944         
34945         // normally this should be '1' as we dont' currently allow multi width columns..
34946         
34947         var colGroup = this._getColGroup( colSpan );
34948         // get the minimum Y value from the columns
34949         var minimumY = Math.min.apply( Math, colGroup );
34950         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34951         
34952         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34953          
34954         // position the brick
34955         var position = {
34956             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34957             y: this.currentSize.y + minimumY + this.padHeight
34958         };
34959         
34960         Roo.log(position);
34961         // apply setHeight to necessary columns
34962         var setHeight = minimumY + sz.height + this.padHeight;
34963         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34964         
34965         var setSpan = this.cols + 1 - colGroup.length;
34966         for ( var i = 0; i < setSpan; i++ ) {
34967           this.colYs[ shortColIndex + i ] = setHeight ;
34968         }
34969       
34970         return position;
34971     },
34972     
34973     /**
34974      * @param {Number} colSpan - number of columns the element spans
34975      * @returns {Array} colGroup
34976      */
34977     _getColGroup : function( colSpan )
34978     {
34979         if ( colSpan < 2 ) {
34980           // if brick spans only one column, use all the column Ys
34981           return this.colYs;
34982         }
34983       
34984         var colGroup = [];
34985         // how many different places could this brick fit horizontally
34986         var groupCount = this.cols + 1 - colSpan;
34987         // for each group potential horizontal position
34988         for ( var i = 0; i < groupCount; i++ ) {
34989           // make an array of colY values for that one group
34990           var groupColYs = this.colYs.slice( i, i + colSpan );
34991           // and get the max value of the array
34992           colGroup[i] = Math.max.apply( Math, groupColYs );
34993         }
34994         return colGroup;
34995     },
34996     /*
34997     _manageStamp : function( stamp )
34998     {
34999         var stampSize =  stamp.getSize();
35000         var offset = stamp.getBox();
35001         // get the columns that this stamp affects
35002         var firstX = this.isOriginLeft ? offset.x : offset.right;
35003         var lastX = firstX + stampSize.width;
35004         var firstCol = Math.floor( firstX / this.columnWidth );
35005         firstCol = Math.max( 0, firstCol );
35006         
35007         var lastCol = Math.floor( lastX / this.columnWidth );
35008         // lastCol should not go over if multiple of columnWidth #425
35009         lastCol -= lastX % this.columnWidth ? 0 : 1;
35010         lastCol = Math.min( this.cols - 1, lastCol );
35011         
35012         // set colYs to bottom of the stamp
35013         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35014             stampSize.height;
35015             
35016         for ( var i = firstCol; i <= lastCol; i++ ) {
35017           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35018         }
35019     },
35020     */
35021     
35022     _getContainerSize : function()
35023     {
35024         this.maxY = Math.max.apply( Math, this.colYs );
35025         var size = {
35026             height: this.maxY
35027         };
35028       
35029         if ( this.isFitWidth ) {
35030             size.width = this._getContainerFitWidth();
35031         }
35032       
35033         return size;
35034     },
35035     
35036     _getContainerFitWidth : function()
35037     {
35038         var unusedCols = 0;
35039         // count unused columns
35040         var i = this.cols;
35041         while ( --i ) {
35042           if ( this.colYs[i] !== 0 ) {
35043             break;
35044           }
35045           unusedCols++;
35046         }
35047         // fit container to columns that have been used
35048         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35049     },
35050     
35051     needsResizeLayout : function()
35052     {
35053         var previousWidth = this.containerWidth;
35054         this.getContainerWidth();
35055         return previousWidth !== this.containerWidth;
35056     }
35057  
35058 });
35059
35060  
35061
35062  /*
35063  * - LGPL
35064  *
35065  * element
35066  * 
35067  */
35068
35069 /**
35070  * @class Roo.bootstrap.MasonryBrick
35071  * @extends Roo.bootstrap.Component
35072  * Bootstrap MasonryBrick class
35073  * 
35074  * @constructor
35075  * Create a new MasonryBrick
35076  * @param {Object} config The config object
35077  */
35078
35079 Roo.bootstrap.MasonryBrick = function(config){
35080     
35081     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35082     
35083     Roo.bootstrap.MasonryBrick.register(this);
35084     
35085     this.addEvents({
35086         // raw events
35087         /**
35088          * @event click
35089          * When a MasonryBrick is clcik
35090          * @param {Roo.bootstrap.MasonryBrick} this
35091          * @param {Roo.EventObject} e
35092          */
35093         "click" : true
35094     });
35095 };
35096
35097 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35098     
35099     /**
35100      * @cfg {String} title
35101      */   
35102     title : '',
35103     /**
35104      * @cfg {String} html
35105      */   
35106     html : '',
35107     /**
35108      * @cfg {String} bgimage
35109      */   
35110     bgimage : '',
35111     /**
35112      * @cfg {String} videourl
35113      */   
35114     videourl : '',
35115     /**
35116      * @cfg {String} cls
35117      */   
35118     cls : '',
35119     /**
35120      * @cfg {String} href
35121      */   
35122     href : '',
35123     /**
35124      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35125      */   
35126     size : 'xs',
35127     
35128     /**
35129      * @cfg {String} placetitle (center|bottom)
35130      */   
35131     placetitle : '',
35132     
35133     /**
35134      * @cfg {Boolean} isFitContainer defalut true
35135      */   
35136     isFitContainer : true, 
35137     
35138     /**
35139      * @cfg {Boolean} preventDefault defalut false
35140      */   
35141     preventDefault : false, 
35142     
35143     /**
35144      * @cfg {Boolean} inverse defalut false
35145      */   
35146     maskInverse : false, 
35147     
35148     getAutoCreate : function()
35149     {
35150         if(!this.isFitContainer){
35151             return this.getSplitAutoCreate();
35152         }
35153         
35154         var cls = 'masonry-brick masonry-brick-full';
35155         
35156         if(this.href.length){
35157             cls += ' masonry-brick-link';
35158         }
35159         
35160         if(this.bgimage.length){
35161             cls += ' masonry-brick-image';
35162         }
35163         
35164         if(this.maskInverse){
35165             cls += ' mask-inverse';
35166         }
35167         
35168         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35169             cls += ' enable-mask';
35170         }
35171         
35172         if(this.size){
35173             cls += ' masonry-' + this.size + '-brick';
35174         }
35175         
35176         if(this.placetitle.length){
35177             
35178             switch (this.placetitle) {
35179                 case 'center' :
35180                     cls += ' masonry-center-title';
35181                     break;
35182                 case 'bottom' :
35183                     cls += ' masonry-bottom-title';
35184                     break;
35185                 default:
35186                     break;
35187             }
35188             
35189         } else {
35190             if(!this.html.length && !this.bgimage.length){
35191                 cls += ' masonry-center-title';
35192             }
35193
35194             if(!this.html.length && this.bgimage.length){
35195                 cls += ' masonry-bottom-title';
35196             }
35197         }
35198         
35199         if(this.cls){
35200             cls += ' ' + this.cls;
35201         }
35202         
35203         var cfg = {
35204             tag: (this.href.length) ? 'a' : 'div',
35205             cls: cls,
35206             cn: [
35207                 {
35208                     tag: 'div',
35209                     cls: 'masonry-brick-mask'
35210                 },
35211                 {
35212                     tag: 'div',
35213                     cls: 'masonry-brick-paragraph',
35214                     cn: []
35215                 }
35216             ]
35217         };
35218         
35219         if(this.href.length){
35220             cfg.href = this.href;
35221         }
35222         
35223         var cn = cfg.cn[1].cn;
35224         
35225         if(this.title.length){
35226             cn.push({
35227                 tag: 'h4',
35228                 cls: 'masonry-brick-title',
35229                 html: this.title
35230             });
35231         }
35232         
35233         if(this.html.length){
35234             cn.push({
35235                 tag: 'p',
35236                 cls: 'masonry-brick-text',
35237                 html: this.html
35238             });
35239         }
35240         
35241         if (!this.title.length && !this.html.length) {
35242             cfg.cn[1].cls += ' hide';
35243         }
35244         
35245         if(this.bgimage.length){
35246             cfg.cn.push({
35247                 tag: 'img',
35248                 cls: 'masonry-brick-image-view',
35249                 src: this.bgimage
35250             });
35251         }
35252         
35253         if(this.videourl.length){
35254             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35255             // youtube support only?
35256             cfg.cn.push({
35257                 tag: 'iframe',
35258                 cls: 'masonry-brick-image-view',
35259                 src: vurl,
35260                 frameborder : 0,
35261                 allowfullscreen : true
35262             });
35263         }
35264         
35265         return cfg;
35266         
35267     },
35268     
35269     getSplitAutoCreate : function()
35270     {
35271         var cls = 'masonry-brick masonry-brick-split';
35272         
35273         if(this.href.length){
35274             cls += ' masonry-brick-link';
35275         }
35276         
35277         if(this.bgimage.length){
35278             cls += ' masonry-brick-image';
35279         }
35280         
35281         if(this.size){
35282             cls += ' masonry-' + this.size + '-brick';
35283         }
35284         
35285         switch (this.placetitle) {
35286             case 'center' :
35287                 cls += ' masonry-center-title';
35288                 break;
35289             case 'bottom' :
35290                 cls += ' masonry-bottom-title';
35291                 break;
35292             default:
35293                 if(!this.bgimage.length){
35294                     cls += ' masonry-center-title';
35295                 }
35296
35297                 if(this.bgimage.length){
35298                     cls += ' masonry-bottom-title';
35299                 }
35300                 break;
35301         }
35302         
35303         if(this.cls){
35304             cls += ' ' + this.cls;
35305         }
35306         
35307         var cfg = {
35308             tag: (this.href.length) ? 'a' : 'div',
35309             cls: cls,
35310             cn: [
35311                 {
35312                     tag: 'div',
35313                     cls: 'masonry-brick-split-head',
35314                     cn: [
35315                         {
35316                             tag: 'div',
35317                             cls: 'masonry-brick-paragraph',
35318                             cn: []
35319                         }
35320                     ]
35321                 },
35322                 {
35323                     tag: 'div',
35324                     cls: 'masonry-brick-split-body',
35325                     cn: []
35326                 }
35327             ]
35328         };
35329         
35330         if(this.href.length){
35331             cfg.href = this.href;
35332         }
35333         
35334         if(this.title.length){
35335             cfg.cn[0].cn[0].cn.push({
35336                 tag: 'h4',
35337                 cls: 'masonry-brick-title',
35338                 html: this.title
35339             });
35340         }
35341         
35342         if(this.html.length){
35343             cfg.cn[1].cn.push({
35344                 tag: 'p',
35345                 cls: 'masonry-brick-text',
35346                 html: this.html
35347             });
35348         }
35349
35350         if(this.bgimage.length){
35351             cfg.cn[0].cn.push({
35352                 tag: 'img',
35353                 cls: 'masonry-brick-image-view',
35354                 src: this.bgimage
35355             });
35356         }
35357         
35358         if(this.videourl.length){
35359             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35360             // youtube support only?
35361             cfg.cn[0].cn.cn.push({
35362                 tag: 'iframe',
35363                 cls: 'masonry-brick-image-view',
35364                 src: vurl,
35365                 frameborder : 0,
35366                 allowfullscreen : true
35367             });
35368         }
35369         
35370         return cfg;
35371     },
35372     
35373     initEvents: function() 
35374     {
35375         switch (this.size) {
35376             case 'xs' :
35377                 this.x = 1;
35378                 this.y = 1;
35379                 break;
35380             case 'sm' :
35381                 this.x = 2;
35382                 this.y = 2;
35383                 break;
35384             case 'md' :
35385             case 'md-left' :
35386             case 'md-right' :
35387                 this.x = 3;
35388                 this.y = 3;
35389                 break;
35390             case 'tall' :
35391                 this.x = 2;
35392                 this.y = 3;
35393                 break;
35394             case 'wide' :
35395                 this.x = 3;
35396                 this.y = 2;
35397                 break;
35398             case 'wide-thin' :
35399                 this.x = 3;
35400                 this.y = 1;
35401                 break;
35402                         
35403             default :
35404                 break;
35405         }
35406         
35407         if(Roo.isTouch){
35408             this.el.on('touchstart', this.onTouchStart, this);
35409             this.el.on('touchmove', this.onTouchMove, this);
35410             this.el.on('touchend', this.onTouchEnd, this);
35411             this.el.on('contextmenu', this.onContextMenu, this);
35412         } else {
35413             this.el.on('mouseenter'  ,this.enter, this);
35414             this.el.on('mouseleave', this.leave, this);
35415             this.el.on('click', this.onClick, this);
35416         }
35417         
35418         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35419             this.parent().bricks.push(this);   
35420         }
35421         
35422     },
35423     
35424     onClick: function(e, el)
35425     {
35426         var time = this.endTimer - this.startTimer;
35427         // Roo.log(e.preventDefault());
35428         if(Roo.isTouch){
35429             if(time > 1000){
35430                 e.preventDefault();
35431                 return;
35432             }
35433         }
35434         
35435         if(!this.preventDefault){
35436             return;
35437         }
35438         
35439         e.preventDefault();
35440         
35441         if (this.activeClass != '') {
35442             this.selectBrick();
35443         }
35444         
35445         this.fireEvent('click', this, e);
35446     },
35447     
35448     enter: function(e, el)
35449     {
35450         e.preventDefault();
35451         
35452         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35453             return;
35454         }
35455         
35456         if(this.bgimage.length && this.html.length){
35457             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35458         }
35459     },
35460     
35461     leave: function(e, el)
35462     {
35463         e.preventDefault();
35464         
35465         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35466             return;
35467         }
35468         
35469         if(this.bgimage.length && this.html.length){
35470             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35471         }
35472     },
35473     
35474     onTouchStart: function(e, el)
35475     {
35476 //        e.preventDefault();
35477         
35478         this.touchmoved = false;
35479         
35480         if(!this.isFitContainer){
35481             return;
35482         }
35483         
35484         if(!this.bgimage.length || !this.html.length){
35485             return;
35486         }
35487         
35488         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35489         
35490         this.timer = new Date().getTime();
35491         
35492     },
35493     
35494     onTouchMove: function(e, el)
35495     {
35496         this.touchmoved = true;
35497     },
35498     
35499     onContextMenu : function(e,el)
35500     {
35501         e.preventDefault();
35502         e.stopPropagation();
35503         return false;
35504     },
35505     
35506     onTouchEnd: function(e, el)
35507     {
35508 //        e.preventDefault();
35509         
35510         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35511         
35512             this.leave(e,el);
35513             
35514             return;
35515         }
35516         
35517         if(!this.bgimage.length || !this.html.length){
35518             
35519             if(this.href.length){
35520                 window.location.href = this.href;
35521             }
35522             
35523             return;
35524         }
35525         
35526         if(!this.isFitContainer){
35527             return;
35528         }
35529         
35530         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35531         
35532         window.location.href = this.href;
35533     },
35534     
35535     //selection on single brick only
35536     selectBrick : function() {
35537         
35538         if (!this.parentId) {
35539             return;
35540         }
35541         
35542         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35543         var index = m.selectedBrick.indexOf(this.id);
35544         
35545         if ( index > -1) {
35546             m.selectedBrick.splice(index,1);
35547             this.el.removeClass(this.activeClass);
35548             return;
35549         }
35550         
35551         for(var i = 0; i < m.selectedBrick.length; i++) {
35552             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35553             b.el.removeClass(b.activeClass);
35554         }
35555         
35556         m.selectedBrick = [];
35557         
35558         m.selectedBrick.push(this.id);
35559         this.el.addClass(this.activeClass);
35560         return;
35561     },
35562     
35563     isSelected : function(){
35564         return this.el.hasClass(this.activeClass);
35565         
35566     }
35567 });
35568
35569 Roo.apply(Roo.bootstrap.MasonryBrick, {
35570     
35571     //groups: {},
35572     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35573      /**
35574     * register a Masonry Brick
35575     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35576     */
35577     
35578     register : function(brick)
35579     {
35580         //this.groups[brick.id] = brick;
35581         this.groups.add(brick.id, brick);
35582     },
35583     /**
35584     * fetch a  masonry brick based on the masonry brick ID
35585     * @param {string} the masonry brick to add
35586     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35587     */
35588     
35589     get: function(brick_id) 
35590     {
35591         // if (typeof(this.groups[brick_id]) == 'undefined') {
35592         //     return false;
35593         // }
35594         // return this.groups[brick_id] ;
35595         
35596         if(this.groups.key(brick_id)) {
35597             return this.groups.key(brick_id);
35598         }
35599         
35600         return false;
35601     }
35602     
35603     
35604     
35605 });
35606
35607  /*
35608  * - LGPL
35609  *
35610  * element
35611  * 
35612  */
35613
35614 /**
35615  * @class Roo.bootstrap.Brick
35616  * @extends Roo.bootstrap.Component
35617  * Bootstrap Brick class
35618  * 
35619  * @constructor
35620  * Create a new Brick
35621  * @param {Object} config The config object
35622  */
35623
35624 Roo.bootstrap.Brick = function(config){
35625     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35626     
35627     this.addEvents({
35628         // raw events
35629         /**
35630          * @event click
35631          * When a Brick is click
35632          * @param {Roo.bootstrap.Brick} this
35633          * @param {Roo.EventObject} e
35634          */
35635         "click" : true
35636     });
35637 };
35638
35639 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35640     
35641     /**
35642      * @cfg {String} title
35643      */   
35644     title : '',
35645     /**
35646      * @cfg {String} html
35647      */   
35648     html : '',
35649     /**
35650      * @cfg {String} bgimage
35651      */   
35652     bgimage : '',
35653     /**
35654      * @cfg {String} cls
35655      */   
35656     cls : '',
35657     /**
35658      * @cfg {String} href
35659      */   
35660     href : '',
35661     /**
35662      * @cfg {String} video
35663      */   
35664     video : '',
35665     /**
35666      * @cfg {Boolean} square
35667      */   
35668     square : true,
35669     
35670     getAutoCreate : function()
35671     {
35672         var cls = 'roo-brick';
35673         
35674         if(this.href.length){
35675             cls += ' roo-brick-link';
35676         }
35677         
35678         if(this.bgimage.length){
35679             cls += ' roo-brick-image';
35680         }
35681         
35682         if(!this.html.length && !this.bgimage.length){
35683             cls += ' roo-brick-center-title';
35684         }
35685         
35686         if(!this.html.length && this.bgimage.length){
35687             cls += ' roo-brick-bottom-title';
35688         }
35689         
35690         if(this.cls){
35691             cls += ' ' + this.cls;
35692         }
35693         
35694         var cfg = {
35695             tag: (this.href.length) ? 'a' : 'div',
35696             cls: cls,
35697             cn: [
35698                 {
35699                     tag: 'div',
35700                     cls: 'roo-brick-paragraph',
35701                     cn: []
35702                 }
35703             ]
35704         };
35705         
35706         if(this.href.length){
35707             cfg.href = this.href;
35708         }
35709         
35710         var cn = cfg.cn[0].cn;
35711         
35712         if(this.title.length){
35713             cn.push({
35714                 tag: 'h4',
35715                 cls: 'roo-brick-title',
35716                 html: this.title
35717             });
35718         }
35719         
35720         if(this.html.length){
35721             cn.push({
35722                 tag: 'p',
35723                 cls: 'roo-brick-text',
35724                 html: this.html
35725             });
35726         } else {
35727             cn.cls += ' hide';
35728         }
35729         
35730         if(this.bgimage.length){
35731             cfg.cn.push({
35732                 tag: 'img',
35733                 cls: 'roo-brick-image-view',
35734                 src: this.bgimage
35735             });
35736         }
35737         
35738         return cfg;
35739     },
35740     
35741     initEvents: function() 
35742     {
35743         if(this.title.length || this.html.length){
35744             this.el.on('mouseenter'  ,this.enter, this);
35745             this.el.on('mouseleave', this.leave, this);
35746         }
35747         
35748         Roo.EventManager.onWindowResize(this.resize, this); 
35749         
35750         if(this.bgimage.length){
35751             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35752             this.imageEl.on('load', this.onImageLoad, this);
35753             return;
35754         }
35755         
35756         this.resize();
35757     },
35758     
35759     onImageLoad : function()
35760     {
35761         this.resize();
35762     },
35763     
35764     resize : function()
35765     {
35766         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35767         
35768         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35769         
35770         if(this.bgimage.length){
35771             var image = this.el.select('.roo-brick-image-view', true).first();
35772             
35773             image.setWidth(paragraph.getWidth());
35774             
35775             if(this.square){
35776                 image.setHeight(paragraph.getWidth());
35777             }
35778             
35779             this.el.setHeight(image.getHeight());
35780             paragraph.setHeight(image.getHeight());
35781             
35782         }
35783         
35784     },
35785     
35786     enter: function(e, el)
35787     {
35788         e.preventDefault();
35789         
35790         if(this.bgimage.length){
35791             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35792             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35793         }
35794     },
35795     
35796     leave: function(e, el)
35797     {
35798         e.preventDefault();
35799         
35800         if(this.bgimage.length){
35801             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35802             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35803         }
35804     }
35805     
35806 });
35807
35808  
35809
35810  /*
35811  * - LGPL
35812  *
35813  * Number field 
35814  */
35815
35816 /**
35817  * @class Roo.bootstrap.NumberField
35818  * @extends Roo.bootstrap.Input
35819  * Bootstrap NumberField class
35820  * 
35821  * 
35822  * 
35823  * 
35824  * @constructor
35825  * Create a new NumberField
35826  * @param {Object} config The config object
35827  */
35828
35829 Roo.bootstrap.NumberField = function(config){
35830     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35831 };
35832
35833 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35834     
35835     /**
35836      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35837      */
35838     allowDecimals : true,
35839     /**
35840      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35841      */
35842     decimalSeparator : ".",
35843     /**
35844      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35845      */
35846     decimalPrecision : 2,
35847     /**
35848      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35849      */
35850     allowNegative : true,
35851     
35852     /**
35853      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35854      */
35855     allowZero: true,
35856     /**
35857      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35858      */
35859     minValue : Number.NEGATIVE_INFINITY,
35860     /**
35861      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35862      */
35863     maxValue : Number.MAX_VALUE,
35864     /**
35865      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35866      */
35867     minText : "The minimum value for this field is {0}",
35868     /**
35869      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35870      */
35871     maxText : "The maximum value for this field is {0}",
35872     /**
35873      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35874      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35875      */
35876     nanText : "{0} is not a valid number",
35877     /**
35878      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35879      */
35880     thousandsDelimiter : false,
35881     /**
35882      * @cfg {String} valueAlign alignment of value
35883      */
35884     valueAlign : "left",
35885
35886     getAutoCreate : function()
35887     {
35888         var hiddenInput = {
35889             tag: 'input',
35890             type: 'hidden',
35891             id: Roo.id(),
35892             cls: 'hidden-number-input'
35893         };
35894         
35895         if (this.name) {
35896             hiddenInput.name = this.name;
35897         }
35898         
35899         this.name = '';
35900         
35901         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35902         
35903         this.name = hiddenInput.name;
35904         
35905         if(cfg.cn.length > 0) {
35906             cfg.cn.push(hiddenInput);
35907         }
35908         
35909         return cfg;
35910     },
35911
35912     // private
35913     initEvents : function()
35914     {   
35915         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35916         
35917         var allowed = "0123456789";
35918         
35919         if(this.allowDecimals){
35920             allowed += this.decimalSeparator;
35921         }
35922         
35923         if(this.allowNegative){
35924             allowed += "-";
35925         }
35926         
35927         if(this.thousandsDelimiter) {
35928             allowed += ",";
35929         }
35930         
35931         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35932         
35933         var keyPress = function(e){
35934             
35935             var k = e.getKey();
35936             
35937             var c = e.getCharCode();
35938             
35939             if(
35940                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35941                     allowed.indexOf(String.fromCharCode(c)) === -1
35942             ){
35943                 e.stopEvent();
35944                 return;
35945             }
35946             
35947             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35948                 return;
35949             }
35950             
35951             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35952                 e.stopEvent();
35953             }
35954         };
35955         
35956         this.el.on("keypress", keyPress, this);
35957     },
35958     
35959     validateValue : function(value)
35960     {
35961         
35962         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35963             return false;
35964         }
35965         
35966         var num = this.parseValue(value);
35967         
35968         if(isNaN(num)){
35969             this.markInvalid(String.format(this.nanText, value));
35970             return false;
35971         }
35972         
35973         if(num < this.minValue){
35974             this.markInvalid(String.format(this.minText, this.minValue));
35975             return false;
35976         }
35977         
35978         if(num > this.maxValue){
35979             this.markInvalid(String.format(this.maxText, this.maxValue));
35980             return false;
35981         }
35982         
35983         return true;
35984     },
35985
35986     getValue : function()
35987     {
35988         var v = this.hiddenEl().getValue();
35989         
35990         return this.fixPrecision(this.parseValue(v));
35991     },
35992
35993     parseValue : function(value)
35994     {
35995         if(this.thousandsDelimiter) {
35996             value += "";
35997             r = new RegExp(",", "g");
35998             value = value.replace(r, "");
35999         }
36000         
36001         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36002         return isNaN(value) ? '' : value;
36003     },
36004
36005     fixPrecision : function(value)
36006     {
36007         if(this.thousandsDelimiter) {
36008             value += "";
36009             r = new RegExp(",", "g");
36010             value = value.replace(r, "");
36011         }
36012         
36013         var nan = isNaN(value);
36014         
36015         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36016             return nan ? '' : value;
36017         }
36018         return parseFloat(value).toFixed(this.decimalPrecision);
36019     },
36020
36021     setValue : function(v)
36022     {
36023         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36024         
36025         this.value = v;
36026         
36027         if(this.rendered){
36028             
36029             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36030             
36031             this.inputEl().dom.value = (v == '') ? '' :
36032                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36033             
36034             if(!this.allowZero && v === '0') {
36035                 this.hiddenEl().dom.value = '';
36036                 this.inputEl().dom.value = '';
36037             }
36038             
36039             this.validate();
36040         }
36041     },
36042
36043     decimalPrecisionFcn : function(v)
36044     {
36045         return Math.floor(v);
36046     },
36047
36048     beforeBlur : function()
36049     {
36050         var v = this.parseValue(this.getRawValue());
36051         
36052         if(v || v === 0 || v === ''){
36053             this.setValue(v);
36054         }
36055     },
36056     
36057     hiddenEl : function()
36058     {
36059         return this.el.select('input.hidden-number-input',true).first();
36060     }
36061     
36062 });
36063
36064  
36065
36066 /*
36067 * Licence: LGPL
36068 */
36069
36070 /**
36071  * @class Roo.bootstrap.DocumentSlider
36072  * @extends Roo.bootstrap.Component
36073  * Bootstrap DocumentSlider class
36074  * 
36075  * @constructor
36076  * Create a new DocumentViewer
36077  * @param {Object} config The config object
36078  */
36079
36080 Roo.bootstrap.DocumentSlider = function(config){
36081     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36082     
36083     this.files = [];
36084     
36085     this.addEvents({
36086         /**
36087          * @event initial
36088          * Fire after initEvent
36089          * @param {Roo.bootstrap.DocumentSlider} this
36090          */
36091         "initial" : true,
36092         /**
36093          * @event update
36094          * Fire after update
36095          * @param {Roo.bootstrap.DocumentSlider} this
36096          */
36097         "update" : true,
36098         /**
36099          * @event click
36100          * Fire after click
36101          * @param {Roo.bootstrap.DocumentSlider} this
36102          */
36103         "click" : true
36104     });
36105 };
36106
36107 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36108     
36109     files : false,
36110     
36111     indicator : 0,
36112     
36113     getAutoCreate : function()
36114     {
36115         var cfg = {
36116             tag : 'div',
36117             cls : 'roo-document-slider',
36118             cn : [
36119                 {
36120                     tag : 'div',
36121                     cls : 'roo-document-slider-header',
36122                     cn : [
36123                         {
36124                             tag : 'div',
36125                             cls : 'roo-document-slider-header-title'
36126                         }
36127                     ]
36128                 },
36129                 {
36130                     tag : 'div',
36131                     cls : 'roo-document-slider-body',
36132                     cn : [
36133                         {
36134                             tag : 'div',
36135                             cls : 'roo-document-slider-prev',
36136                             cn : [
36137                                 {
36138                                     tag : 'i',
36139                                     cls : 'fa fa-chevron-left'
36140                                 }
36141                             ]
36142                         },
36143                         {
36144                             tag : 'div',
36145                             cls : 'roo-document-slider-thumb',
36146                             cn : [
36147                                 {
36148                                     tag : 'img',
36149                                     cls : 'roo-document-slider-image'
36150                                 }
36151                             ]
36152                         },
36153                         {
36154                             tag : 'div',
36155                             cls : 'roo-document-slider-next',
36156                             cn : [
36157                                 {
36158                                     tag : 'i',
36159                                     cls : 'fa fa-chevron-right'
36160                                 }
36161                             ]
36162                         }
36163                     ]
36164                 }
36165             ]
36166         };
36167         
36168         return cfg;
36169     },
36170     
36171     initEvents : function()
36172     {
36173         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36174         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36175         
36176         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36177         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36178         
36179         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36180         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36181         
36182         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36183         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36184         
36185         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36186         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36187         
36188         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36189         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36190         
36191         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36192         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36193         
36194         this.thumbEl.on('click', this.onClick, this);
36195         
36196         this.prevIndicator.on('click', this.prev, this);
36197         
36198         this.nextIndicator.on('click', this.next, this);
36199         
36200     },
36201     
36202     initial : function()
36203     {
36204         if(this.files.length){
36205             this.indicator = 1;
36206             this.update()
36207         }
36208         
36209         this.fireEvent('initial', this);
36210     },
36211     
36212     update : function()
36213     {
36214         this.imageEl.attr('src', this.files[this.indicator - 1]);
36215         
36216         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36217         
36218         this.prevIndicator.show();
36219         
36220         if(this.indicator == 1){
36221             this.prevIndicator.hide();
36222         }
36223         
36224         this.nextIndicator.show();
36225         
36226         if(this.indicator == this.files.length){
36227             this.nextIndicator.hide();
36228         }
36229         
36230         this.thumbEl.scrollTo('top');
36231         
36232         this.fireEvent('update', this);
36233     },
36234     
36235     onClick : function(e)
36236     {
36237         e.preventDefault();
36238         
36239         this.fireEvent('click', this);
36240     },
36241     
36242     prev : function(e)
36243     {
36244         e.preventDefault();
36245         
36246         this.indicator = Math.max(1, this.indicator - 1);
36247         
36248         this.update();
36249     },
36250     
36251     next : function(e)
36252     {
36253         e.preventDefault();
36254         
36255         this.indicator = Math.min(this.files.length, this.indicator + 1);
36256         
36257         this.update();
36258     }
36259 });
36260 /*
36261  * - LGPL
36262  *
36263  * RadioSet
36264  *
36265  *
36266  */
36267
36268 /**
36269  * @class Roo.bootstrap.RadioSet
36270  * @extends Roo.bootstrap.Input
36271  * Bootstrap RadioSet class
36272  * @cfg {String} indicatorpos (left|right) default left
36273  * @cfg {Boolean} inline (true|false) inline the element (default true)
36274  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36275  * @constructor
36276  * Create a new RadioSet
36277  * @param {Object} config The config object
36278  */
36279
36280 Roo.bootstrap.RadioSet = function(config){
36281     
36282     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36283     
36284     this.radioes = [];
36285     
36286     Roo.bootstrap.RadioSet.register(this);
36287     
36288     this.addEvents({
36289         /**
36290         * @event check
36291         * Fires when the element is checked or unchecked.
36292         * @param {Roo.bootstrap.RadioSet} this This radio
36293         * @param {Roo.bootstrap.Radio} item The checked item
36294         */
36295        check : true,
36296        /**
36297         * @event click
36298         * Fires when the element is click.
36299         * @param {Roo.bootstrap.RadioSet} this This radio set
36300         * @param {Roo.bootstrap.Radio} item The checked item
36301         * @param {Roo.EventObject} e The event object
36302         */
36303        click : true
36304     });
36305     
36306 };
36307
36308 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36309
36310     radioes : false,
36311     
36312     inline : true,
36313     
36314     weight : '',
36315     
36316     indicatorpos : 'left',
36317     
36318     getAutoCreate : function()
36319     {
36320         var label = {
36321             tag : 'label',
36322             cls : 'roo-radio-set-label',
36323             cn : [
36324                 {
36325                     tag : 'span',
36326                     html : this.fieldLabel
36327                 }
36328             ]
36329         };
36330         if (Roo.bootstrap.version == 3) {
36331             
36332             
36333             if(this.indicatorpos == 'left'){
36334                 label.cn.unshift({
36335                     tag : 'i',
36336                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36337                     tooltip : 'This field is required'
36338                 });
36339             } else {
36340                 label.cn.push({
36341                     tag : 'i',
36342                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36343                     tooltip : 'This field is required'
36344                 });
36345             }
36346         }
36347         var items = {
36348             tag : 'div',
36349             cls : 'roo-radio-set-items'
36350         };
36351         
36352         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36353         
36354         if (align === 'left' && this.fieldLabel.length) {
36355             
36356             items = {
36357                 cls : "roo-radio-set-right", 
36358                 cn: [
36359                     items
36360                 ]
36361             };
36362             
36363             if(this.labelWidth > 12){
36364                 label.style = "width: " + this.labelWidth + 'px';
36365             }
36366             
36367             if(this.labelWidth < 13 && this.labelmd == 0){
36368                 this.labelmd = this.labelWidth;
36369             }
36370             
36371             if(this.labellg > 0){
36372                 label.cls += ' col-lg-' + this.labellg;
36373                 items.cls += ' col-lg-' + (12 - this.labellg);
36374             }
36375             
36376             if(this.labelmd > 0){
36377                 label.cls += ' col-md-' + this.labelmd;
36378                 items.cls += ' col-md-' + (12 - this.labelmd);
36379             }
36380             
36381             if(this.labelsm > 0){
36382                 label.cls += ' col-sm-' + this.labelsm;
36383                 items.cls += ' col-sm-' + (12 - this.labelsm);
36384             }
36385             
36386             if(this.labelxs > 0){
36387                 label.cls += ' col-xs-' + this.labelxs;
36388                 items.cls += ' col-xs-' + (12 - this.labelxs);
36389             }
36390         }
36391         
36392         var cfg = {
36393             tag : 'div',
36394             cls : 'roo-radio-set',
36395             cn : [
36396                 {
36397                     tag : 'input',
36398                     cls : 'roo-radio-set-input',
36399                     type : 'hidden',
36400                     name : this.name,
36401                     value : this.value ? this.value :  ''
36402                 },
36403                 label,
36404                 items
36405             ]
36406         };
36407         
36408         if(this.weight.length){
36409             cfg.cls += ' roo-radio-' + this.weight;
36410         }
36411         
36412         if(this.inline) {
36413             cfg.cls += ' roo-radio-set-inline';
36414         }
36415         
36416         var settings=this;
36417         ['xs','sm','md','lg'].map(function(size){
36418             if (settings[size]) {
36419                 cfg.cls += ' col-' + size + '-' + settings[size];
36420             }
36421         });
36422         
36423         return cfg;
36424         
36425     },
36426
36427     initEvents : function()
36428     {
36429         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36430         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36431         
36432         if(!this.fieldLabel.length){
36433             this.labelEl.hide();
36434         }
36435         
36436         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36437         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36438         
36439         this.indicator = this.indicatorEl();
36440         
36441         if(this.indicator){
36442             this.indicator.addClass('invisible');
36443         }
36444         
36445         this.originalValue = this.getValue();
36446         
36447     },
36448     
36449     inputEl: function ()
36450     {
36451         return this.el.select('.roo-radio-set-input', true).first();
36452     },
36453     
36454     getChildContainer : function()
36455     {
36456         return this.itemsEl;
36457     },
36458     
36459     register : function(item)
36460     {
36461         this.radioes.push(item);
36462         
36463     },
36464     
36465     validate : function()
36466     {   
36467         if(this.getVisibilityEl().hasClass('hidden')){
36468             return true;
36469         }
36470         
36471         var valid = false;
36472         
36473         Roo.each(this.radioes, function(i){
36474             if(!i.checked){
36475                 return;
36476             }
36477             
36478             valid = true;
36479             return false;
36480         });
36481         
36482         if(this.allowBlank) {
36483             return true;
36484         }
36485         
36486         if(this.disabled || valid){
36487             this.markValid();
36488             return true;
36489         }
36490         
36491         this.markInvalid();
36492         return false;
36493         
36494     },
36495     
36496     markValid : function()
36497     {
36498         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36499             this.indicatorEl().removeClass('visible');
36500             this.indicatorEl().addClass('invisible');
36501         }
36502         
36503         
36504         if (Roo.bootstrap.version == 3) {
36505             this.el.removeClass([this.invalidClass, this.validClass]);
36506             this.el.addClass(this.validClass);
36507         } else {
36508             this.el.removeClass(['is-invalid','is-valid']);
36509             this.el.addClass(['is-valid']);
36510         }
36511         this.fireEvent('valid', this);
36512     },
36513     
36514     markInvalid : function(msg)
36515     {
36516         if(this.allowBlank || this.disabled){
36517             return;
36518         }
36519         
36520         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36521             this.indicatorEl().removeClass('invisible');
36522             this.indicatorEl().addClass('visible');
36523         }
36524         if (Roo.bootstrap.version == 3) {
36525             this.el.removeClass([this.invalidClass, this.validClass]);
36526             this.el.addClass(this.invalidClass);
36527         } else {
36528             this.el.removeClass(['is-invalid','is-valid']);
36529             this.el.addClass(['is-invalid']);
36530         }
36531         
36532         this.fireEvent('invalid', this, msg);
36533         
36534     },
36535     
36536     setValue : function(v, suppressEvent)
36537     {   
36538         if(this.value === v){
36539             return;
36540         }
36541         
36542         this.value = v;
36543         
36544         if(this.rendered){
36545             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36546         }
36547         
36548         Roo.each(this.radioes, function(i){
36549             i.checked = false;
36550             i.el.removeClass('checked');
36551         });
36552         
36553         Roo.each(this.radioes, function(i){
36554             
36555             if(i.value === v || i.value.toString() === v.toString()){
36556                 i.checked = true;
36557                 i.el.addClass('checked');
36558                 
36559                 if(suppressEvent !== true){
36560                     this.fireEvent('check', this, i);
36561                 }
36562                 
36563                 return false;
36564             }
36565             
36566         }, this);
36567         
36568         this.validate();
36569     },
36570     
36571     clearInvalid : function(){
36572         
36573         if(!this.el || this.preventMark){
36574             return;
36575         }
36576         
36577         this.el.removeClass([this.invalidClass]);
36578         
36579         this.fireEvent('valid', this);
36580     }
36581     
36582 });
36583
36584 Roo.apply(Roo.bootstrap.RadioSet, {
36585     
36586     groups: {},
36587     
36588     register : function(set)
36589     {
36590         this.groups[set.name] = set;
36591     },
36592     
36593     get: function(name) 
36594     {
36595         if (typeof(this.groups[name]) == 'undefined') {
36596             return false;
36597         }
36598         
36599         return this.groups[name] ;
36600     }
36601     
36602 });
36603 /*
36604  * Based on:
36605  * Ext JS Library 1.1.1
36606  * Copyright(c) 2006-2007, Ext JS, LLC.
36607  *
36608  * Originally Released Under LGPL - original licence link has changed is not relivant.
36609  *
36610  * Fork - LGPL
36611  * <script type="text/javascript">
36612  */
36613
36614
36615 /**
36616  * @class Roo.bootstrap.SplitBar
36617  * @extends Roo.util.Observable
36618  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36619  * <br><br>
36620  * Usage:
36621  * <pre><code>
36622 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36623                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36624 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36625 split.minSize = 100;
36626 split.maxSize = 600;
36627 split.animate = true;
36628 split.on('moved', splitterMoved);
36629 </code></pre>
36630  * @constructor
36631  * Create a new SplitBar
36632  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36633  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36634  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36635  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36636                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36637                         position of the SplitBar).
36638  */
36639 Roo.bootstrap.SplitBar = function(cfg){
36640     
36641     /** @private */
36642     
36643     //{
36644     //  dragElement : elm
36645     //  resizingElement: el,
36646         // optional..
36647     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36648     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36649         // existingProxy ???
36650     //}
36651     
36652     this.el = Roo.get(cfg.dragElement, true);
36653     this.el.dom.unselectable = "on";
36654     /** @private */
36655     this.resizingEl = Roo.get(cfg.resizingElement, true);
36656
36657     /**
36658      * @private
36659      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36660      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36661      * @type Number
36662      */
36663     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36664     
36665     /**
36666      * The minimum size of the resizing element. (Defaults to 0)
36667      * @type Number
36668      */
36669     this.minSize = 0;
36670     
36671     /**
36672      * The maximum size of the resizing element. (Defaults to 2000)
36673      * @type Number
36674      */
36675     this.maxSize = 2000;
36676     
36677     /**
36678      * Whether to animate the transition to the new size
36679      * @type Boolean
36680      */
36681     this.animate = false;
36682     
36683     /**
36684      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36685      * @type Boolean
36686      */
36687     this.useShim = false;
36688     
36689     /** @private */
36690     this.shim = null;
36691     
36692     if(!cfg.existingProxy){
36693         /** @private */
36694         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36695     }else{
36696         this.proxy = Roo.get(cfg.existingProxy).dom;
36697     }
36698     /** @private */
36699     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36700     
36701     /** @private */
36702     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36703     
36704     /** @private */
36705     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36706     
36707     /** @private */
36708     this.dragSpecs = {};
36709     
36710     /**
36711      * @private The adapter to use to positon and resize elements
36712      */
36713     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36714     this.adapter.init(this);
36715     
36716     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36717         /** @private */
36718         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36719         this.el.addClass("roo-splitbar-h");
36720     }else{
36721         /** @private */
36722         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36723         this.el.addClass("roo-splitbar-v");
36724     }
36725     
36726     this.addEvents({
36727         /**
36728          * @event resize
36729          * Fires when the splitter is moved (alias for {@link #event-moved})
36730          * @param {Roo.bootstrap.SplitBar} this
36731          * @param {Number} newSize the new width or height
36732          */
36733         "resize" : true,
36734         /**
36735          * @event moved
36736          * Fires when the splitter is moved
36737          * @param {Roo.bootstrap.SplitBar} this
36738          * @param {Number} newSize the new width or height
36739          */
36740         "moved" : true,
36741         /**
36742          * @event beforeresize
36743          * Fires before the splitter is dragged
36744          * @param {Roo.bootstrap.SplitBar} this
36745          */
36746         "beforeresize" : true,
36747
36748         "beforeapply" : true
36749     });
36750
36751     Roo.util.Observable.call(this);
36752 };
36753
36754 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36755     onStartProxyDrag : function(x, y){
36756         this.fireEvent("beforeresize", this);
36757         if(!this.overlay){
36758             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36759             o.unselectable();
36760             o.enableDisplayMode("block");
36761             // all splitbars share the same overlay
36762             Roo.bootstrap.SplitBar.prototype.overlay = o;
36763         }
36764         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36765         this.overlay.show();
36766         Roo.get(this.proxy).setDisplayed("block");
36767         var size = this.adapter.getElementSize(this);
36768         this.activeMinSize = this.getMinimumSize();;
36769         this.activeMaxSize = this.getMaximumSize();;
36770         var c1 = size - this.activeMinSize;
36771         var c2 = Math.max(this.activeMaxSize - size, 0);
36772         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36773             this.dd.resetConstraints();
36774             this.dd.setXConstraint(
36775                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36776                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36777             );
36778             this.dd.setYConstraint(0, 0);
36779         }else{
36780             this.dd.resetConstraints();
36781             this.dd.setXConstraint(0, 0);
36782             this.dd.setYConstraint(
36783                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36784                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36785             );
36786          }
36787         this.dragSpecs.startSize = size;
36788         this.dragSpecs.startPoint = [x, y];
36789         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36790     },
36791     
36792     /** 
36793      * @private Called after the drag operation by the DDProxy
36794      */
36795     onEndProxyDrag : function(e){
36796         Roo.get(this.proxy).setDisplayed(false);
36797         var endPoint = Roo.lib.Event.getXY(e);
36798         if(this.overlay){
36799             this.overlay.hide();
36800         }
36801         var newSize;
36802         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36803             newSize = this.dragSpecs.startSize + 
36804                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36805                     endPoint[0] - this.dragSpecs.startPoint[0] :
36806                     this.dragSpecs.startPoint[0] - endPoint[0]
36807                 );
36808         }else{
36809             newSize = this.dragSpecs.startSize + 
36810                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36811                     endPoint[1] - this.dragSpecs.startPoint[1] :
36812                     this.dragSpecs.startPoint[1] - endPoint[1]
36813                 );
36814         }
36815         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36816         if(newSize != this.dragSpecs.startSize){
36817             if(this.fireEvent('beforeapply', this, newSize) !== false){
36818                 this.adapter.setElementSize(this, newSize);
36819                 this.fireEvent("moved", this, newSize);
36820                 this.fireEvent("resize", this, newSize);
36821             }
36822         }
36823     },
36824     
36825     /**
36826      * Get the adapter this SplitBar uses
36827      * @return The adapter object
36828      */
36829     getAdapter : function(){
36830         return this.adapter;
36831     },
36832     
36833     /**
36834      * Set the adapter this SplitBar uses
36835      * @param {Object} adapter A SplitBar adapter object
36836      */
36837     setAdapter : function(adapter){
36838         this.adapter = adapter;
36839         this.adapter.init(this);
36840     },
36841     
36842     /**
36843      * Gets the minimum size for the resizing element
36844      * @return {Number} The minimum size
36845      */
36846     getMinimumSize : function(){
36847         return this.minSize;
36848     },
36849     
36850     /**
36851      * Sets the minimum size for the resizing element
36852      * @param {Number} minSize The minimum size
36853      */
36854     setMinimumSize : function(minSize){
36855         this.minSize = minSize;
36856     },
36857     
36858     /**
36859      * Gets the maximum size for the resizing element
36860      * @return {Number} The maximum size
36861      */
36862     getMaximumSize : function(){
36863         return this.maxSize;
36864     },
36865     
36866     /**
36867      * Sets the maximum size for the resizing element
36868      * @param {Number} maxSize The maximum size
36869      */
36870     setMaximumSize : function(maxSize){
36871         this.maxSize = maxSize;
36872     },
36873     
36874     /**
36875      * Sets the initialize size for the resizing element
36876      * @param {Number} size The initial size
36877      */
36878     setCurrentSize : function(size){
36879         var oldAnimate = this.animate;
36880         this.animate = false;
36881         this.adapter.setElementSize(this, size);
36882         this.animate = oldAnimate;
36883     },
36884     
36885     /**
36886      * Destroy this splitbar. 
36887      * @param {Boolean} removeEl True to remove the element
36888      */
36889     destroy : function(removeEl){
36890         if(this.shim){
36891             this.shim.remove();
36892         }
36893         this.dd.unreg();
36894         this.proxy.parentNode.removeChild(this.proxy);
36895         if(removeEl){
36896             this.el.remove();
36897         }
36898     }
36899 });
36900
36901 /**
36902  * @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.
36903  */
36904 Roo.bootstrap.SplitBar.createProxy = function(dir){
36905     var proxy = new Roo.Element(document.createElement("div"));
36906     proxy.unselectable();
36907     var cls = 'roo-splitbar-proxy';
36908     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36909     document.body.appendChild(proxy.dom);
36910     return proxy.dom;
36911 };
36912
36913 /** 
36914  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36915  * Default Adapter. It assumes the splitter and resizing element are not positioned
36916  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36917  */
36918 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36919 };
36920
36921 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36922     // do nothing for now
36923     init : function(s){
36924     
36925     },
36926     /**
36927      * Called before drag operations to get the current size of the resizing element. 
36928      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36929      */
36930      getElementSize : function(s){
36931         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36932             return s.resizingEl.getWidth();
36933         }else{
36934             return s.resizingEl.getHeight();
36935         }
36936     },
36937     
36938     /**
36939      * Called after drag operations to set the size of the resizing element.
36940      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36941      * @param {Number} newSize The new size to set
36942      * @param {Function} onComplete A function to be invoked when resizing is complete
36943      */
36944     setElementSize : function(s, newSize, onComplete){
36945         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36946             if(!s.animate){
36947                 s.resizingEl.setWidth(newSize);
36948                 if(onComplete){
36949                     onComplete(s, newSize);
36950                 }
36951             }else{
36952                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36953             }
36954         }else{
36955             
36956             if(!s.animate){
36957                 s.resizingEl.setHeight(newSize);
36958                 if(onComplete){
36959                     onComplete(s, newSize);
36960                 }
36961             }else{
36962                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36963             }
36964         }
36965     }
36966 };
36967
36968 /** 
36969  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36970  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36971  * Adapter that  moves the splitter element to align with the resized sizing element. 
36972  * Used with an absolute positioned SplitBar.
36973  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36974  * document.body, make sure you assign an id to the body element.
36975  */
36976 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36977     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36978     this.container = Roo.get(container);
36979 };
36980
36981 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36982     init : function(s){
36983         this.basic.init(s);
36984     },
36985     
36986     getElementSize : function(s){
36987         return this.basic.getElementSize(s);
36988     },
36989     
36990     setElementSize : function(s, newSize, onComplete){
36991         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36992     },
36993     
36994     moveSplitter : function(s){
36995         var yes = Roo.bootstrap.SplitBar;
36996         switch(s.placement){
36997             case yes.LEFT:
36998                 s.el.setX(s.resizingEl.getRight());
36999                 break;
37000             case yes.RIGHT:
37001                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37002                 break;
37003             case yes.TOP:
37004                 s.el.setY(s.resizingEl.getBottom());
37005                 break;
37006             case yes.BOTTOM:
37007                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37008                 break;
37009         }
37010     }
37011 };
37012
37013 /**
37014  * Orientation constant - Create a vertical SplitBar
37015  * @static
37016  * @type Number
37017  */
37018 Roo.bootstrap.SplitBar.VERTICAL = 1;
37019
37020 /**
37021  * Orientation constant - Create a horizontal SplitBar
37022  * @static
37023  * @type Number
37024  */
37025 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37026
37027 /**
37028  * Placement constant - The resizing element is to the left of the splitter element
37029  * @static
37030  * @type Number
37031  */
37032 Roo.bootstrap.SplitBar.LEFT = 1;
37033
37034 /**
37035  * Placement constant - The resizing element is to the right of the splitter element
37036  * @static
37037  * @type Number
37038  */
37039 Roo.bootstrap.SplitBar.RIGHT = 2;
37040
37041 /**
37042  * Placement constant - The resizing element is positioned above the splitter element
37043  * @static
37044  * @type Number
37045  */
37046 Roo.bootstrap.SplitBar.TOP = 3;
37047
37048 /**
37049  * Placement constant - The resizing element is positioned under splitter element
37050  * @static
37051  * @type Number
37052  */
37053 Roo.bootstrap.SplitBar.BOTTOM = 4;
37054 Roo.namespace("Roo.bootstrap.layout");/*
37055  * Based on:
37056  * Ext JS Library 1.1.1
37057  * Copyright(c) 2006-2007, Ext JS, LLC.
37058  *
37059  * Originally Released Under LGPL - original licence link has changed is not relivant.
37060  *
37061  * Fork - LGPL
37062  * <script type="text/javascript">
37063  */
37064
37065 /**
37066  * @class Roo.bootstrap.layout.Manager
37067  * @extends Roo.bootstrap.Component
37068  * Base class for layout managers.
37069  */
37070 Roo.bootstrap.layout.Manager = function(config)
37071 {
37072     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37073
37074
37075
37076
37077
37078     /** false to disable window resize monitoring @type Boolean */
37079     this.monitorWindowResize = true;
37080     this.regions = {};
37081     this.addEvents({
37082         /**
37083          * @event layout
37084          * Fires when a layout is performed.
37085          * @param {Roo.LayoutManager} this
37086          */
37087         "layout" : true,
37088         /**
37089          * @event regionresized
37090          * Fires when the user resizes a region.
37091          * @param {Roo.LayoutRegion} region The resized region
37092          * @param {Number} newSize The new size (width for east/west, height for north/south)
37093          */
37094         "regionresized" : true,
37095         /**
37096          * @event regioncollapsed
37097          * Fires when a region is collapsed.
37098          * @param {Roo.LayoutRegion} region The collapsed region
37099          */
37100         "regioncollapsed" : true,
37101         /**
37102          * @event regionexpanded
37103          * Fires when a region is expanded.
37104          * @param {Roo.LayoutRegion} region The expanded region
37105          */
37106         "regionexpanded" : true
37107     });
37108     this.updating = false;
37109
37110     if (config.el) {
37111         this.el = Roo.get(config.el);
37112         this.initEvents();
37113     }
37114
37115 };
37116
37117 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37118
37119
37120     regions : null,
37121
37122     monitorWindowResize : true,
37123
37124
37125     updating : false,
37126
37127
37128     onRender : function(ct, position)
37129     {
37130         if(!this.el){
37131             this.el = Roo.get(ct);
37132             this.initEvents();
37133         }
37134         //this.fireEvent('render',this);
37135     },
37136
37137
37138     initEvents: function()
37139     {
37140
37141
37142         // ie scrollbar fix
37143         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37144             document.body.scroll = "no";
37145         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37146             this.el.position('relative');
37147         }
37148         this.id = this.el.id;
37149         this.el.addClass("roo-layout-container");
37150         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37151         if(this.el.dom != document.body ) {
37152             this.el.on('resize', this.layout,this);
37153             this.el.on('show', this.layout,this);
37154         }
37155
37156     },
37157
37158     /**
37159      * Returns true if this layout is currently being updated
37160      * @return {Boolean}
37161      */
37162     isUpdating : function(){
37163         return this.updating;
37164     },
37165
37166     /**
37167      * Suspend the LayoutManager from doing auto-layouts while
37168      * making multiple add or remove calls
37169      */
37170     beginUpdate : function(){
37171         this.updating = true;
37172     },
37173
37174     /**
37175      * Restore auto-layouts and optionally disable the manager from performing a layout
37176      * @param {Boolean} noLayout true to disable a layout update
37177      */
37178     endUpdate : function(noLayout){
37179         this.updating = false;
37180         if(!noLayout){
37181             this.layout();
37182         }
37183     },
37184
37185     layout: function(){
37186         // abstract...
37187     },
37188
37189     onRegionResized : function(region, newSize){
37190         this.fireEvent("regionresized", region, newSize);
37191         this.layout();
37192     },
37193
37194     onRegionCollapsed : function(region){
37195         this.fireEvent("regioncollapsed", region);
37196     },
37197
37198     onRegionExpanded : function(region){
37199         this.fireEvent("regionexpanded", region);
37200     },
37201
37202     /**
37203      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37204      * performs box-model adjustments.
37205      * @return {Object} The size as an object {width: (the width), height: (the height)}
37206      */
37207     getViewSize : function()
37208     {
37209         var size;
37210         if(this.el.dom != document.body){
37211             size = this.el.getSize();
37212         }else{
37213             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37214         }
37215         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37216         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37217         return size;
37218     },
37219
37220     /**
37221      * Returns the Element this layout is bound to.
37222      * @return {Roo.Element}
37223      */
37224     getEl : function(){
37225         return this.el;
37226     },
37227
37228     /**
37229      * Returns the specified region.
37230      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37231      * @return {Roo.LayoutRegion}
37232      */
37233     getRegion : function(target){
37234         return this.regions[target.toLowerCase()];
37235     },
37236
37237     onWindowResize : function(){
37238         if(this.monitorWindowResize){
37239             this.layout();
37240         }
37241     }
37242 });
37243 /*
37244  * Based on:
37245  * Ext JS Library 1.1.1
37246  * Copyright(c) 2006-2007, Ext JS, LLC.
37247  *
37248  * Originally Released Under LGPL - original licence link has changed is not relivant.
37249  *
37250  * Fork - LGPL
37251  * <script type="text/javascript">
37252  */
37253 /**
37254  * @class Roo.bootstrap.layout.Border
37255  * @extends Roo.bootstrap.layout.Manager
37256  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37257  * please see: examples/bootstrap/nested.html<br><br>
37258  
37259 <b>The container the layout is rendered into can be either the body element or any other element.
37260 If it is not the body element, the container needs to either be an absolute positioned element,
37261 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37262 the container size if it is not the body element.</b>
37263
37264 * @constructor
37265 * Create a new Border
37266 * @param {Object} config Configuration options
37267  */
37268 Roo.bootstrap.layout.Border = function(config){
37269     config = config || {};
37270     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37271     
37272     
37273     
37274     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37275         if(config[region]){
37276             config[region].region = region;
37277             this.addRegion(config[region]);
37278         }
37279     },this);
37280     
37281 };
37282
37283 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37284
37285 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37286     
37287     parent : false, // this might point to a 'nest' or a ???
37288     
37289     /**
37290      * Creates and adds a new region if it doesn't already exist.
37291      * @param {String} target The target region key (north, south, east, west or center).
37292      * @param {Object} config The regions config object
37293      * @return {BorderLayoutRegion} The new region
37294      */
37295     addRegion : function(config)
37296     {
37297         if(!this.regions[config.region]){
37298             var r = this.factory(config);
37299             this.bindRegion(r);
37300         }
37301         return this.regions[config.region];
37302     },
37303
37304     // private (kinda)
37305     bindRegion : function(r){
37306         this.regions[r.config.region] = r;
37307         
37308         r.on("visibilitychange",    this.layout, this);
37309         r.on("paneladded",          this.layout, this);
37310         r.on("panelremoved",        this.layout, this);
37311         r.on("invalidated",         this.layout, this);
37312         r.on("resized",             this.onRegionResized, this);
37313         r.on("collapsed",           this.onRegionCollapsed, this);
37314         r.on("expanded",            this.onRegionExpanded, this);
37315     },
37316
37317     /**
37318      * Performs a layout update.
37319      */
37320     layout : function()
37321     {
37322         if(this.updating) {
37323             return;
37324         }
37325         
37326         // render all the rebions if they have not been done alreayd?
37327         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37328             if(this.regions[region] && !this.regions[region].bodyEl){
37329                 this.regions[region].onRender(this.el)
37330             }
37331         },this);
37332         
37333         var size = this.getViewSize();
37334         var w = size.width;
37335         var h = size.height;
37336         var centerW = w;
37337         var centerH = h;
37338         var centerY = 0;
37339         var centerX = 0;
37340         //var x = 0, y = 0;
37341
37342         var rs = this.regions;
37343         var north = rs["north"];
37344         var south = rs["south"]; 
37345         var west = rs["west"];
37346         var east = rs["east"];
37347         var center = rs["center"];
37348         //if(this.hideOnLayout){ // not supported anymore
37349             //c.el.setStyle("display", "none");
37350         //}
37351         if(north && north.isVisible()){
37352             var b = north.getBox();
37353             var m = north.getMargins();
37354             b.width = w - (m.left+m.right);
37355             b.x = m.left;
37356             b.y = m.top;
37357             centerY = b.height + b.y + m.bottom;
37358             centerH -= centerY;
37359             north.updateBox(this.safeBox(b));
37360         }
37361         if(south && south.isVisible()){
37362             var b = south.getBox();
37363             var m = south.getMargins();
37364             b.width = w - (m.left+m.right);
37365             b.x = m.left;
37366             var totalHeight = (b.height + m.top + m.bottom);
37367             b.y = h - totalHeight + m.top;
37368             centerH -= totalHeight;
37369             south.updateBox(this.safeBox(b));
37370         }
37371         if(west && west.isVisible()){
37372             var b = west.getBox();
37373             var m = west.getMargins();
37374             b.height = centerH - (m.top+m.bottom);
37375             b.x = m.left;
37376             b.y = centerY + m.top;
37377             var totalWidth = (b.width + m.left + m.right);
37378             centerX += totalWidth;
37379             centerW -= totalWidth;
37380             west.updateBox(this.safeBox(b));
37381         }
37382         if(east && east.isVisible()){
37383             var b = east.getBox();
37384             var m = east.getMargins();
37385             b.height = centerH - (m.top+m.bottom);
37386             var totalWidth = (b.width + m.left + m.right);
37387             b.x = w - totalWidth + m.left;
37388             b.y = centerY + m.top;
37389             centerW -= totalWidth;
37390             east.updateBox(this.safeBox(b));
37391         }
37392         if(center){
37393             var m = center.getMargins();
37394             var centerBox = {
37395                 x: centerX + m.left,
37396                 y: centerY + m.top,
37397                 width: centerW - (m.left+m.right),
37398                 height: centerH - (m.top+m.bottom)
37399             };
37400             //if(this.hideOnLayout){
37401                 //center.el.setStyle("display", "block");
37402             //}
37403             center.updateBox(this.safeBox(centerBox));
37404         }
37405         this.el.repaint();
37406         this.fireEvent("layout", this);
37407     },
37408
37409     // private
37410     safeBox : function(box){
37411         box.width = Math.max(0, box.width);
37412         box.height = Math.max(0, box.height);
37413         return box;
37414     },
37415
37416     /**
37417      * Adds a ContentPanel (or subclass) to this layout.
37418      * @param {String} target The target region key (north, south, east, west or center).
37419      * @param {Roo.ContentPanel} panel The panel to add
37420      * @return {Roo.ContentPanel} The added panel
37421      */
37422     add : function(target, panel){
37423          
37424         target = target.toLowerCase();
37425         return this.regions[target].add(panel);
37426     },
37427
37428     /**
37429      * Remove a ContentPanel (or subclass) to this layout.
37430      * @param {String} target The target region key (north, south, east, west or center).
37431      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37432      * @return {Roo.ContentPanel} The removed panel
37433      */
37434     remove : function(target, panel){
37435         target = target.toLowerCase();
37436         return this.regions[target].remove(panel);
37437     },
37438
37439     /**
37440      * Searches all regions for a panel with the specified id
37441      * @param {String} panelId
37442      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37443      */
37444     findPanel : function(panelId){
37445         var rs = this.regions;
37446         for(var target in rs){
37447             if(typeof rs[target] != "function"){
37448                 var p = rs[target].getPanel(panelId);
37449                 if(p){
37450                     return p;
37451                 }
37452             }
37453         }
37454         return null;
37455     },
37456
37457     /**
37458      * Searches all regions for a panel with the specified id and activates (shows) it.
37459      * @param {String/ContentPanel} panelId The panels id or the panel itself
37460      * @return {Roo.ContentPanel} The shown panel or null
37461      */
37462     showPanel : function(panelId) {
37463       var rs = this.regions;
37464       for(var target in rs){
37465          var r = rs[target];
37466          if(typeof r != "function"){
37467             if(r.hasPanel(panelId)){
37468                return r.showPanel(panelId);
37469             }
37470          }
37471       }
37472       return null;
37473    },
37474
37475    /**
37476      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37477      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37478      */
37479    /*
37480     restoreState : function(provider){
37481         if(!provider){
37482             provider = Roo.state.Manager;
37483         }
37484         var sm = new Roo.LayoutStateManager();
37485         sm.init(this, provider);
37486     },
37487 */
37488  
37489  
37490     /**
37491      * Adds a xtype elements to the layout.
37492      * <pre><code>
37493
37494 layout.addxtype({
37495        xtype : 'ContentPanel',
37496        region: 'west',
37497        items: [ .... ]
37498    }
37499 );
37500
37501 layout.addxtype({
37502         xtype : 'NestedLayoutPanel',
37503         region: 'west',
37504         layout: {
37505            center: { },
37506            west: { }   
37507         },
37508         items : [ ... list of content panels or nested layout panels.. ]
37509    }
37510 );
37511 </code></pre>
37512      * @param {Object} cfg Xtype definition of item to add.
37513      */
37514     addxtype : function(cfg)
37515     {
37516         // basically accepts a pannel...
37517         // can accept a layout region..!?!?
37518         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37519         
37520         
37521         // theory?  children can only be panels??
37522         
37523         //if (!cfg.xtype.match(/Panel$/)) {
37524         //    return false;
37525         //}
37526         var ret = false;
37527         
37528         if (typeof(cfg.region) == 'undefined') {
37529             Roo.log("Failed to add Panel, region was not set");
37530             Roo.log(cfg);
37531             return false;
37532         }
37533         var region = cfg.region;
37534         delete cfg.region;
37535         
37536           
37537         var xitems = [];
37538         if (cfg.items) {
37539             xitems = cfg.items;
37540             delete cfg.items;
37541         }
37542         var nb = false;
37543         
37544         if ( region == 'center') {
37545             Roo.log("Center: " + cfg.title);
37546         }
37547         
37548         
37549         switch(cfg.xtype) 
37550         {
37551             case 'Content':  // ContentPanel (el, cfg)
37552             case 'Scroll':  // ContentPanel (el, cfg)
37553             case 'View': 
37554                 cfg.autoCreate = cfg.autoCreate || true;
37555                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37556                 //} else {
37557                 //    var el = this.el.createChild();
37558                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37559                 //}
37560                 
37561                 this.add(region, ret);
37562                 break;
37563             
37564             /*
37565             case 'TreePanel': // our new panel!
37566                 cfg.el = this.el.createChild();
37567                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37568                 this.add(region, ret);
37569                 break;
37570             */
37571             
37572             case 'Nest': 
37573                 // create a new Layout (which is  a Border Layout...
37574                 
37575                 var clayout = cfg.layout;
37576                 clayout.el  = this.el.createChild();
37577                 clayout.items   = clayout.items  || [];
37578                 
37579                 delete cfg.layout;
37580                 
37581                 // replace this exitems with the clayout ones..
37582                 xitems = clayout.items;
37583                  
37584                 // force background off if it's in center...
37585                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37586                     cfg.background = false;
37587                 }
37588                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37589                 
37590                 
37591                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37592                 //console.log('adding nested layout panel '  + cfg.toSource());
37593                 this.add(region, ret);
37594                 nb = {}; /// find first...
37595                 break;
37596             
37597             case 'Grid':
37598                 
37599                 // needs grid and region
37600                 
37601                 //var el = this.getRegion(region).el.createChild();
37602                 /*
37603                  *var el = this.el.createChild();
37604                 // create the grid first...
37605                 cfg.grid.container = el;
37606                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37607                 */
37608                 
37609                 if (region == 'center' && this.active ) {
37610                     cfg.background = false;
37611                 }
37612                 
37613                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37614                 
37615                 this.add(region, ret);
37616                 /*
37617                 if (cfg.background) {
37618                     // render grid on panel activation (if panel background)
37619                     ret.on('activate', function(gp) {
37620                         if (!gp.grid.rendered) {
37621                     //        gp.grid.render(el);
37622                         }
37623                     });
37624                 } else {
37625                   //  cfg.grid.render(el);
37626                 }
37627                 */
37628                 break;
37629            
37630            
37631             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37632                 // it was the old xcomponent building that caused this before.
37633                 // espeically if border is the top element in the tree.
37634                 ret = this;
37635                 break; 
37636                 
37637                     
37638                 
37639                 
37640                 
37641             default:
37642                 /*
37643                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37644                     
37645                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37646                     this.add(region, ret);
37647                 } else {
37648                 */
37649                     Roo.log(cfg);
37650                     throw "Can not add '" + cfg.xtype + "' to Border";
37651                     return null;
37652              
37653                                 
37654              
37655         }
37656         this.beginUpdate();
37657         // add children..
37658         var region = '';
37659         var abn = {};
37660         Roo.each(xitems, function(i)  {
37661             region = nb && i.region ? i.region : false;
37662             
37663             var add = ret.addxtype(i);
37664            
37665             if (region) {
37666                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37667                 if (!i.background) {
37668                     abn[region] = nb[region] ;
37669                 }
37670             }
37671             
37672         });
37673         this.endUpdate();
37674
37675         // make the last non-background panel active..
37676         //if (nb) { Roo.log(abn); }
37677         if (nb) {
37678             
37679             for(var r in abn) {
37680                 region = this.getRegion(r);
37681                 if (region) {
37682                     // tried using nb[r], but it does not work..
37683                      
37684                     region.showPanel(abn[r]);
37685                    
37686                 }
37687             }
37688         }
37689         return ret;
37690         
37691     },
37692     
37693     
37694 // private
37695     factory : function(cfg)
37696     {
37697         
37698         var validRegions = Roo.bootstrap.layout.Border.regions;
37699
37700         var target = cfg.region;
37701         cfg.mgr = this;
37702         
37703         var r = Roo.bootstrap.layout;
37704         Roo.log(target);
37705         switch(target){
37706             case "north":
37707                 return new r.North(cfg);
37708             case "south":
37709                 return new r.South(cfg);
37710             case "east":
37711                 return new r.East(cfg);
37712             case "west":
37713                 return new r.West(cfg);
37714             case "center":
37715                 return new r.Center(cfg);
37716         }
37717         throw 'Layout region "'+target+'" not supported.';
37718     }
37719     
37720     
37721 });
37722  /*
37723  * Based on:
37724  * Ext JS Library 1.1.1
37725  * Copyright(c) 2006-2007, Ext JS, LLC.
37726  *
37727  * Originally Released Under LGPL - original licence link has changed is not relivant.
37728  *
37729  * Fork - LGPL
37730  * <script type="text/javascript">
37731  */
37732  
37733 /**
37734  * @class Roo.bootstrap.layout.Basic
37735  * @extends Roo.util.Observable
37736  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37737  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37738  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37739  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37740  * @cfg {string}   region  the region that it inhabits..
37741  * @cfg {bool}   skipConfig skip config?
37742  * 
37743
37744  */
37745 Roo.bootstrap.layout.Basic = function(config){
37746     
37747     this.mgr = config.mgr;
37748     
37749     this.position = config.region;
37750     
37751     var skipConfig = config.skipConfig;
37752     
37753     this.events = {
37754         /**
37755          * @scope Roo.BasicLayoutRegion
37756          */
37757         
37758         /**
37759          * @event beforeremove
37760          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37761          * @param {Roo.LayoutRegion} this
37762          * @param {Roo.ContentPanel} panel The panel
37763          * @param {Object} e The cancel event object
37764          */
37765         "beforeremove" : true,
37766         /**
37767          * @event invalidated
37768          * Fires when the layout for this region is changed.
37769          * @param {Roo.LayoutRegion} this
37770          */
37771         "invalidated" : true,
37772         /**
37773          * @event visibilitychange
37774          * Fires when this region is shown or hidden 
37775          * @param {Roo.LayoutRegion} this
37776          * @param {Boolean} visibility true or false
37777          */
37778         "visibilitychange" : true,
37779         /**
37780          * @event paneladded
37781          * Fires when a panel is added. 
37782          * @param {Roo.LayoutRegion} this
37783          * @param {Roo.ContentPanel} panel The panel
37784          */
37785         "paneladded" : true,
37786         /**
37787          * @event panelremoved
37788          * Fires when a panel is removed. 
37789          * @param {Roo.LayoutRegion} this
37790          * @param {Roo.ContentPanel} panel The panel
37791          */
37792         "panelremoved" : true,
37793         /**
37794          * @event beforecollapse
37795          * Fires when this region before collapse.
37796          * @param {Roo.LayoutRegion} this
37797          */
37798         "beforecollapse" : true,
37799         /**
37800          * @event collapsed
37801          * Fires when this region is collapsed.
37802          * @param {Roo.LayoutRegion} this
37803          */
37804         "collapsed" : true,
37805         /**
37806          * @event expanded
37807          * Fires when this region is expanded.
37808          * @param {Roo.LayoutRegion} this
37809          */
37810         "expanded" : true,
37811         /**
37812          * @event slideshow
37813          * Fires when this region is slid into view.
37814          * @param {Roo.LayoutRegion} this
37815          */
37816         "slideshow" : true,
37817         /**
37818          * @event slidehide
37819          * Fires when this region slides out of view. 
37820          * @param {Roo.LayoutRegion} this
37821          */
37822         "slidehide" : true,
37823         /**
37824          * @event panelactivated
37825          * Fires when a panel is activated. 
37826          * @param {Roo.LayoutRegion} this
37827          * @param {Roo.ContentPanel} panel The activated panel
37828          */
37829         "panelactivated" : true,
37830         /**
37831          * @event resized
37832          * Fires when the user resizes this region. 
37833          * @param {Roo.LayoutRegion} this
37834          * @param {Number} newSize The new size (width for east/west, height for north/south)
37835          */
37836         "resized" : true
37837     };
37838     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37839     this.panels = new Roo.util.MixedCollection();
37840     this.panels.getKey = this.getPanelId.createDelegate(this);
37841     this.box = null;
37842     this.activePanel = null;
37843     // ensure listeners are added...
37844     
37845     if (config.listeners || config.events) {
37846         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37847             listeners : config.listeners || {},
37848             events : config.events || {}
37849         });
37850     }
37851     
37852     if(skipConfig !== true){
37853         this.applyConfig(config);
37854     }
37855 };
37856
37857 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37858 {
37859     getPanelId : function(p){
37860         return p.getId();
37861     },
37862     
37863     applyConfig : function(config){
37864         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37865         this.config = config;
37866         
37867     },
37868     
37869     /**
37870      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37871      * the width, for horizontal (north, south) the height.
37872      * @param {Number} newSize The new width or height
37873      */
37874     resizeTo : function(newSize){
37875         var el = this.el ? this.el :
37876                  (this.activePanel ? this.activePanel.getEl() : null);
37877         if(el){
37878             switch(this.position){
37879                 case "east":
37880                 case "west":
37881                     el.setWidth(newSize);
37882                     this.fireEvent("resized", this, newSize);
37883                 break;
37884                 case "north":
37885                 case "south":
37886                     el.setHeight(newSize);
37887                     this.fireEvent("resized", this, newSize);
37888                 break;                
37889             }
37890         }
37891     },
37892     
37893     getBox : function(){
37894         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37895     },
37896     
37897     getMargins : function(){
37898         return this.margins;
37899     },
37900     
37901     updateBox : function(box){
37902         this.box = box;
37903         var el = this.activePanel.getEl();
37904         el.dom.style.left = box.x + "px";
37905         el.dom.style.top = box.y + "px";
37906         this.activePanel.setSize(box.width, box.height);
37907     },
37908     
37909     /**
37910      * Returns the container element for this region.
37911      * @return {Roo.Element}
37912      */
37913     getEl : function(){
37914         return this.activePanel;
37915     },
37916     
37917     /**
37918      * Returns true if this region is currently visible.
37919      * @return {Boolean}
37920      */
37921     isVisible : function(){
37922         return this.activePanel ? true : false;
37923     },
37924     
37925     setActivePanel : function(panel){
37926         panel = this.getPanel(panel);
37927         if(this.activePanel && this.activePanel != panel){
37928             this.activePanel.setActiveState(false);
37929             this.activePanel.getEl().setLeftTop(-10000,-10000);
37930         }
37931         this.activePanel = panel;
37932         panel.setActiveState(true);
37933         if(this.box){
37934             panel.setSize(this.box.width, this.box.height);
37935         }
37936         this.fireEvent("panelactivated", this, panel);
37937         this.fireEvent("invalidated");
37938     },
37939     
37940     /**
37941      * Show the specified panel.
37942      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37943      * @return {Roo.ContentPanel} The shown panel or null
37944      */
37945     showPanel : function(panel){
37946         panel = this.getPanel(panel);
37947         if(panel){
37948             this.setActivePanel(panel);
37949         }
37950         return panel;
37951     },
37952     
37953     /**
37954      * Get the active panel for this region.
37955      * @return {Roo.ContentPanel} The active panel or null
37956      */
37957     getActivePanel : function(){
37958         return this.activePanel;
37959     },
37960     
37961     /**
37962      * Add the passed ContentPanel(s)
37963      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37964      * @return {Roo.ContentPanel} The panel added (if only one was added)
37965      */
37966     add : function(panel){
37967         if(arguments.length > 1){
37968             for(var i = 0, len = arguments.length; i < len; i++) {
37969                 this.add(arguments[i]);
37970             }
37971             return null;
37972         }
37973         if(this.hasPanel(panel)){
37974             this.showPanel(panel);
37975             return panel;
37976         }
37977         var el = panel.getEl();
37978         if(el.dom.parentNode != this.mgr.el.dom){
37979             this.mgr.el.dom.appendChild(el.dom);
37980         }
37981         if(panel.setRegion){
37982             panel.setRegion(this);
37983         }
37984         this.panels.add(panel);
37985         el.setStyle("position", "absolute");
37986         if(!panel.background){
37987             this.setActivePanel(panel);
37988             if(this.config.initialSize && this.panels.getCount()==1){
37989                 this.resizeTo(this.config.initialSize);
37990             }
37991         }
37992         this.fireEvent("paneladded", this, panel);
37993         return panel;
37994     },
37995     
37996     /**
37997      * Returns true if the panel is in this region.
37998      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
37999      * @return {Boolean}
38000      */
38001     hasPanel : function(panel){
38002         if(typeof panel == "object"){ // must be panel obj
38003             panel = panel.getId();
38004         }
38005         return this.getPanel(panel) ? true : false;
38006     },
38007     
38008     /**
38009      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38010      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38011      * @param {Boolean} preservePanel Overrides the config preservePanel option
38012      * @return {Roo.ContentPanel} The panel that was removed
38013      */
38014     remove : function(panel, preservePanel){
38015         panel = this.getPanel(panel);
38016         if(!panel){
38017             return null;
38018         }
38019         var e = {};
38020         this.fireEvent("beforeremove", this, panel, e);
38021         if(e.cancel === true){
38022             return null;
38023         }
38024         var panelId = panel.getId();
38025         this.panels.removeKey(panelId);
38026         return panel;
38027     },
38028     
38029     /**
38030      * Returns the panel specified or null if it's not in this region.
38031      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38032      * @return {Roo.ContentPanel}
38033      */
38034     getPanel : function(id){
38035         if(typeof id == "object"){ // must be panel obj
38036             return id;
38037         }
38038         return this.panels.get(id);
38039     },
38040     
38041     /**
38042      * Returns this regions position (north/south/east/west/center).
38043      * @return {String} 
38044      */
38045     getPosition: function(){
38046         return this.position;    
38047     }
38048 });/*
38049  * Based on:
38050  * Ext JS Library 1.1.1
38051  * Copyright(c) 2006-2007, Ext JS, LLC.
38052  *
38053  * Originally Released Under LGPL - original licence link has changed is not relivant.
38054  *
38055  * Fork - LGPL
38056  * <script type="text/javascript">
38057  */
38058  
38059 /**
38060  * @class Roo.bootstrap.layout.Region
38061  * @extends Roo.bootstrap.layout.Basic
38062  * This class represents a region in a layout manager.
38063  
38064  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38065  * @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})
38066  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38067  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38068  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38069  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38070  * @cfg {String}    title           The title for the region (overrides panel titles)
38071  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38072  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38073  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38074  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38075  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38076  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38077  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38078  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38079  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38080  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38081
38082  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38083  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38084  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38085  * @cfg {Number}    width           For East/West panels
38086  * @cfg {Number}    height          For North/South panels
38087  * @cfg {Boolean}   split           To show the splitter
38088  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38089  * 
38090  * @cfg {string}   cls             Extra CSS classes to add to region
38091  * 
38092  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38093  * @cfg {string}   region  the region that it inhabits..
38094  *
38095
38096  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38097  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38098
38099  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38100  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38101  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38102  */
38103 Roo.bootstrap.layout.Region = function(config)
38104 {
38105     this.applyConfig(config);
38106
38107     var mgr = config.mgr;
38108     var pos = config.region;
38109     config.skipConfig = true;
38110     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38111     
38112     if (mgr.el) {
38113         this.onRender(mgr.el);   
38114     }
38115      
38116     this.visible = true;
38117     this.collapsed = false;
38118     this.unrendered_panels = [];
38119 };
38120
38121 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38122
38123     position: '', // set by wrapper (eg. north/south etc..)
38124     unrendered_panels : null,  // unrendered panels.
38125     
38126     tabPosition : false,
38127     
38128     mgr: false, // points to 'Border'
38129     
38130     
38131     createBody : function(){
38132         /** This region's body element 
38133         * @type Roo.Element */
38134         this.bodyEl = this.el.createChild({
38135                 tag: "div",
38136                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38137         });
38138     },
38139
38140     onRender: function(ctr, pos)
38141     {
38142         var dh = Roo.DomHelper;
38143         /** This region's container element 
38144         * @type Roo.Element */
38145         this.el = dh.append(ctr.dom, {
38146                 tag: "div",
38147                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38148             }, true);
38149         /** This region's title element 
38150         * @type Roo.Element */
38151     
38152         this.titleEl = dh.append(this.el.dom,  {
38153                 tag: "div",
38154                 unselectable: "on",
38155                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38156                 children:[
38157                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38158                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38159                 ]
38160             }, true);
38161         
38162         this.titleEl.enableDisplayMode();
38163         /** This region's title text element 
38164         * @type HTMLElement */
38165         this.titleTextEl = this.titleEl.dom.firstChild;
38166         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38167         /*
38168         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38169         this.closeBtn.enableDisplayMode();
38170         this.closeBtn.on("click", this.closeClicked, this);
38171         this.closeBtn.hide();
38172     */
38173         this.createBody(this.config);
38174         if(this.config.hideWhenEmpty){
38175             this.hide();
38176             this.on("paneladded", this.validateVisibility, this);
38177             this.on("panelremoved", this.validateVisibility, this);
38178         }
38179         if(this.autoScroll){
38180             this.bodyEl.setStyle("overflow", "auto");
38181         }else{
38182             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38183         }
38184         //if(c.titlebar !== false){
38185             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38186                 this.titleEl.hide();
38187             }else{
38188                 this.titleEl.show();
38189                 if(this.config.title){
38190                     this.titleTextEl.innerHTML = this.config.title;
38191                 }
38192             }
38193         //}
38194         if(this.config.collapsed){
38195             this.collapse(true);
38196         }
38197         if(this.config.hidden){
38198             this.hide();
38199         }
38200         
38201         if (this.unrendered_panels && this.unrendered_panels.length) {
38202             for (var i =0;i< this.unrendered_panels.length; i++) {
38203                 this.add(this.unrendered_panels[i]);
38204             }
38205             this.unrendered_panels = null;
38206             
38207         }
38208         
38209     },
38210     
38211     applyConfig : function(c)
38212     {
38213         /*
38214          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38215             var dh = Roo.DomHelper;
38216             if(c.titlebar !== false){
38217                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38218                 this.collapseBtn.on("click", this.collapse, this);
38219                 this.collapseBtn.enableDisplayMode();
38220                 /*
38221                 if(c.showPin === true || this.showPin){
38222                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38223                     this.stickBtn.enableDisplayMode();
38224                     this.stickBtn.on("click", this.expand, this);
38225                     this.stickBtn.hide();
38226                 }
38227                 
38228             }
38229             */
38230             /** This region's collapsed element
38231             * @type Roo.Element */
38232             /*
38233              *
38234             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38235                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38236             ]}, true);
38237             
38238             if(c.floatable !== false){
38239                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38240                this.collapsedEl.on("click", this.collapseClick, this);
38241             }
38242
38243             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38244                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38245                    id: "message", unselectable: "on", style:{"float":"left"}});
38246                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38247              }
38248             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38249             this.expandBtn.on("click", this.expand, this);
38250             
38251         }
38252         
38253         if(this.collapseBtn){
38254             this.collapseBtn.setVisible(c.collapsible == true);
38255         }
38256         
38257         this.cmargins = c.cmargins || this.cmargins ||
38258                          (this.position == "west" || this.position == "east" ?
38259                              {top: 0, left: 2, right:2, bottom: 0} :
38260                              {top: 2, left: 0, right:0, bottom: 2});
38261         */
38262         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38263         
38264         
38265         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38266         
38267         this.autoScroll = c.autoScroll || false;
38268         
38269         
38270        
38271         
38272         this.duration = c.duration || .30;
38273         this.slideDuration = c.slideDuration || .45;
38274         this.config = c;
38275        
38276     },
38277     /**
38278      * Returns true if this region is currently visible.
38279      * @return {Boolean}
38280      */
38281     isVisible : function(){
38282         return this.visible;
38283     },
38284
38285     /**
38286      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38287      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38288      */
38289     //setCollapsedTitle : function(title){
38290     //    title = title || "&#160;";
38291      //   if(this.collapsedTitleTextEl){
38292       //      this.collapsedTitleTextEl.innerHTML = title;
38293        // }
38294     //},
38295
38296     getBox : function(){
38297         var b;
38298       //  if(!this.collapsed){
38299             b = this.el.getBox(false, true);
38300        // }else{
38301           //  b = this.collapsedEl.getBox(false, true);
38302         //}
38303         return b;
38304     },
38305
38306     getMargins : function(){
38307         return this.margins;
38308         //return this.collapsed ? this.cmargins : this.margins;
38309     },
38310 /*
38311     highlight : function(){
38312         this.el.addClass("x-layout-panel-dragover");
38313     },
38314
38315     unhighlight : function(){
38316         this.el.removeClass("x-layout-panel-dragover");
38317     },
38318 */
38319     updateBox : function(box)
38320     {
38321         if (!this.bodyEl) {
38322             return; // not rendered yet..
38323         }
38324         
38325         this.box = box;
38326         if(!this.collapsed){
38327             this.el.dom.style.left = box.x + "px";
38328             this.el.dom.style.top = box.y + "px";
38329             this.updateBody(box.width, box.height);
38330         }else{
38331             this.collapsedEl.dom.style.left = box.x + "px";
38332             this.collapsedEl.dom.style.top = box.y + "px";
38333             this.collapsedEl.setSize(box.width, box.height);
38334         }
38335         if(this.tabs){
38336             this.tabs.autoSizeTabs();
38337         }
38338     },
38339
38340     updateBody : function(w, h)
38341     {
38342         if(w !== null){
38343             this.el.setWidth(w);
38344             w -= this.el.getBorderWidth("rl");
38345             if(this.config.adjustments){
38346                 w += this.config.adjustments[0];
38347             }
38348         }
38349         if(h !== null && h > 0){
38350             this.el.setHeight(h);
38351             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38352             h -= this.el.getBorderWidth("tb");
38353             if(this.config.adjustments){
38354                 h += this.config.adjustments[1];
38355             }
38356             this.bodyEl.setHeight(h);
38357             if(this.tabs){
38358                 h = this.tabs.syncHeight(h);
38359             }
38360         }
38361         if(this.panelSize){
38362             w = w !== null ? w : this.panelSize.width;
38363             h = h !== null ? h : this.panelSize.height;
38364         }
38365         if(this.activePanel){
38366             var el = this.activePanel.getEl();
38367             w = w !== null ? w : el.getWidth();
38368             h = h !== null ? h : el.getHeight();
38369             this.panelSize = {width: w, height: h};
38370             this.activePanel.setSize(w, h);
38371         }
38372         if(Roo.isIE && this.tabs){
38373             this.tabs.el.repaint();
38374         }
38375     },
38376
38377     /**
38378      * Returns the container element for this region.
38379      * @return {Roo.Element}
38380      */
38381     getEl : function(){
38382         return this.el;
38383     },
38384
38385     /**
38386      * Hides this region.
38387      */
38388     hide : function(){
38389         //if(!this.collapsed){
38390             this.el.dom.style.left = "-2000px";
38391             this.el.hide();
38392         //}else{
38393          //   this.collapsedEl.dom.style.left = "-2000px";
38394          //   this.collapsedEl.hide();
38395        // }
38396         this.visible = false;
38397         this.fireEvent("visibilitychange", this, false);
38398     },
38399
38400     /**
38401      * Shows this region if it was previously hidden.
38402      */
38403     show : function(){
38404         //if(!this.collapsed){
38405             this.el.show();
38406         //}else{
38407         //    this.collapsedEl.show();
38408        // }
38409         this.visible = true;
38410         this.fireEvent("visibilitychange", this, true);
38411     },
38412 /*
38413     closeClicked : function(){
38414         if(this.activePanel){
38415             this.remove(this.activePanel);
38416         }
38417     },
38418
38419     collapseClick : function(e){
38420         if(this.isSlid){
38421            e.stopPropagation();
38422            this.slideIn();
38423         }else{
38424            e.stopPropagation();
38425            this.slideOut();
38426         }
38427     },
38428 */
38429     /**
38430      * Collapses this region.
38431      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38432      */
38433     /*
38434     collapse : function(skipAnim, skipCheck = false){
38435         if(this.collapsed) {
38436             return;
38437         }
38438         
38439         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38440             
38441             this.collapsed = true;
38442             if(this.split){
38443                 this.split.el.hide();
38444             }
38445             if(this.config.animate && skipAnim !== true){
38446                 this.fireEvent("invalidated", this);
38447                 this.animateCollapse();
38448             }else{
38449                 this.el.setLocation(-20000,-20000);
38450                 this.el.hide();
38451                 this.collapsedEl.show();
38452                 this.fireEvent("collapsed", this);
38453                 this.fireEvent("invalidated", this);
38454             }
38455         }
38456         
38457     },
38458 */
38459     animateCollapse : function(){
38460         // overridden
38461     },
38462
38463     /**
38464      * Expands this region if it was previously collapsed.
38465      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38466      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38467      */
38468     /*
38469     expand : function(e, skipAnim){
38470         if(e) {
38471             e.stopPropagation();
38472         }
38473         if(!this.collapsed || this.el.hasActiveFx()) {
38474             return;
38475         }
38476         if(this.isSlid){
38477             this.afterSlideIn();
38478             skipAnim = true;
38479         }
38480         this.collapsed = false;
38481         if(this.config.animate && skipAnim !== true){
38482             this.animateExpand();
38483         }else{
38484             this.el.show();
38485             if(this.split){
38486                 this.split.el.show();
38487             }
38488             this.collapsedEl.setLocation(-2000,-2000);
38489             this.collapsedEl.hide();
38490             this.fireEvent("invalidated", this);
38491             this.fireEvent("expanded", this);
38492         }
38493     },
38494 */
38495     animateExpand : function(){
38496         // overridden
38497     },
38498
38499     initTabs : function()
38500     {
38501         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38502         
38503         var ts = new Roo.bootstrap.panel.Tabs({
38504             el: this.bodyEl.dom,
38505             region : this,
38506             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38507             disableTooltips: this.config.disableTabTips,
38508             toolbar : this.config.toolbar
38509         });
38510         
38511         if(this.config.hideTabs){
38512             ts.stripWrap.setDisplayed(false);
38513         }
38514         this.tabs = ts;
38515         ts.resizeTabs = this.config.resizeTabs === true;
38516         ts.minTabWidth = this.config.minTabWidth || 40;
38517         ts.maxTabWidth = this.config.maxTabWidth || 250;
38518         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38519         ts.monitorResize = false;
38520         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38521         ts.bodyEl.addClass('roo-layout-tabs-body');
38522         this.panels.each(this.initPanelAsTab, this);
38523     },
38524
38525     initPanelAsTab : function(panel){
38526         var ti = this.tabs.addTab(
38527             panel.getEl().id,
38528             panel.getTitle(),
38529             null,
38530             this.config.closeOnTab && panel.isClosable(),
38531             panel.tpl
38532         );
38533         if(panel.tabTip !== undefined){
38534             ti.setTooltip(panel.tabTip);
38535         }
38536         ti.on("activate", function(){
38537               this.setActivePanel(panel);
38538         }, this);
38539         
38540         if(this.config.closeOnTab){
38541             ti.on("beforeclose", function(t, e){
38542                 e.cancel = true;
38543                 this.remove(panel);
38544             }, this);
38545         }
38546         
38547         panel.tabItem = ti;
38548         
38549         return ti;
38550     },
38551
38552     updatePanelTitle : function(panel, title)
38553     {
38554         if(this.activePanel == panel){
38555             this.updateTitle(title);
38556         }
38557         if(this.tabs){
38558             var ti = this.tabs.getTab(panel.getEl().id);
38559             ti.setText(title);
38560             if(panel.tabTip !== undefined){
38561                 ti.setTooltip(panel.tabTip);
38562             }
38563         }
38564     },
38565
38566     updateTitle : function(title){
38567         if(this.titleTextEl && !this.config.title){
38568             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38569         }
38570     },
38571
38572     setActivePanel : function(panel)
38573     {
38574         panel = this.getPanel(panel);
38575         if(this.activePanel && this.activePanel != panel){
38576             if(this.activePanel.setActiveState(false) === false){
38577                 return;
38578             }
38579         }
38580         this.activePanel = panel;
38581         panel.setActiveState(true);
38582         if(this.panelSize){
38583             panel.setSize(this.panelSize.width, this.panelSize.height);
38584         }
38585         if(this.closeBtn){
38586             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38587         }
38588         this.updateTitle(panel.getTitle());
38589         if(this.tabs){
38590             this.fireEvent("invalidated", this);
38591         }
38592         this.fireEvent("panelactivated", this, panel);
38593     },
38594
38595     /**
38596      * Shows the specified panel.
38597      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38598      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38599      */
38600     showPanel : function(panel)
38601     {
38602         panel = this.getPanel(panel);
38603         if(panel){
38604             if(this.tabs){
38605                 var tab = this.tabs.getTab(panel.getEl().id);
38606                 if(tab.isHidden()){
38607                     this.tabs.unhideTab(tab.id);
38608                 }
38609                 tab.activate();
38610             }else{
38611                 this.setActivePanel(panel);
38612             }
38613         }
38614         return panel;
38615     },
38616
38617     /**
38618      * Get the active panel for this region.
38619      * @return {Roo.ContentPanel} The active panel or null
38620      */
38621     getActivePanel : function(){
38622         return this.activePanel;
38623     },
38624
38625     validateVisibility : function(){
38626         if(this.panels.getCount() < 1){
38627             this.updateTitle("&#160;");
38628             this.closeBtn.hide();
38629             this.hide();
38630         }else{
38631             if(!this.isVisible()){
38632                 this.show();
38633             }
38634         }
38635     },
38636
38637     /**
38638      * Adds the passed ContentPanel(s) to this region.
38639      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38640      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38641      */
38642     add : function(panel)
38643     {
38644         if(arguments.length > 1){
38645             for(var i = 0, len = arguments.length; i < len; i++) {
38646                 this.add(arguments[i]);
38647             }
38648             return null;
38649         }
38650         
38651         // if we have not been rendered yet, then we can not really do much of this..
38652         if (!this.bodyEl) {
38653             this.unrendered_panels.push(panel);
38654             return panel;
38655         }
38656         
38657         
38658         
38659         
38660         if(this.hasPanel(panel)){
38661             this.showPanel(panel);
38662             return panel;
38663         }
38664         panel.setRegion(this);
38665         this.panels.add(panel);
38666        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38667             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38668             // and hide them... ???
38669             this.bodyEl.dom.appendChild(panel.getEl().dom);
38670             if(panel.background !== true){
38671                 this.setActivePanel(panel);
38672             }
38673             this.fireEvent("paneladded", this, panel);
38674             return panel;
38675         }
38676         */
38677         if(!this.tabs){
38678             this.initTabs();
38679         }else{
38680             this.initPanelAsTab(panel);
38681         }
38682         
38683         
38684         if(panel.background !== true){
38685             this.tabs.activate(panel.getEl().id);
38686         }
38687         this.fireEvent("paneladded", this, panel);
38688         return panel;
38689     },
38690
38691     /**
38692      * Hides the tab for the specified panel.
38693      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38694      */
38695     hidePanel : function(panel){
38696         if(this.tabs && (panel = this.getPanel(panel))){
38697             this.tabs.hideTab(panel.getEl().id);
38698         }
38699     },
38700
38701     /**
38702      * Unhides the tab for a previously hidden panel.
38703      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38704      */
38705     unhidePanel : function(panel){
38706         if(this.tabs && (panel = this.getPanel(panel))){
38707             this.tabs.unhideTab(panel.getEl().id);
38708         }
38709     },
38710
38711     clearPanels : function(){
38712         while(this.panels.getCount() > 0){
38713              this.remove(this.panels.first());
38714         }
38715     },
38716
38717     /**
38718      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38719      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38720      * @param {Boolean} preservePanel Overrides the config preservePanel option
38721      * @return {Roo.ContentPanel} The panel that was removed
38722      */
38723     remove : function(panel, preservePanel)
38724     {
38725         panel = this.getPanel(panel);
38726         if(!panel){
38727             return null;
38728         }
38729         var e = {};
38730         this.fireEvent("beforeremove", this, panel, e);
38731         if(e.cancel === true){
38732             return null;
38733         }
38734         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38735         var panelId = panel.getId();
38736         this.panels.removeKey(panelId);
38737         if(preservePanel){
38738             document.body.appendChild(panel.getEl().dom);
38739         }
38740         if(this.tabs){
38741             this.tabs.removeTab(panel.getEl().id);
38742         }else if (!preservePanel){
38743             this.bodyEl.dom.removeChild(panel.getEl().dom);
38744         }
38745         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38746             var p = this.panels.first();
38747             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38748             tempEl.appendChild(p.getEl().dom);
38749             this.bodyEl.update("");
38750             this.bodyEl.dom.appendChild(p.getEl().dom);
38751             tempEl = null;
38752             this.updateTitle(p.getTitle());
38753             this.tabs = null;
38754             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38755             this.setActivePanel(p);
38756         }
38757         panel.setRegion(null);
38758         if(this.activePanel == panel){
38759             this.activePanel = null;
38760         }
38761         if(this.config.autoDestroy !== false && preservePanel !== true){
38762             try{panel.destroy();}catch(e){}
38763         }
38764         this.fireEvent("panelremoved", this, panel);
38765         return panel;
38766     },
38767
38768     /**
38769      * Returns the TabPanel component used by this region
38770      * @return {Roo.TabPanel}
38771      */
38772     getTabs : function(){
38773         return this.tabs;
38774     },
38775
38776     createTool : function(parentEl, className){
38777         var btn = Roo.DomHelper.append(parentEl, {
38778             tag: "div",
38779             cls: "x-layout-tools-button",
38780             children: [ {
38781                 tag: "div",
38782                 cls: "roo-layout-tools-button-inner " + className,
38783                 html: "&#160;"
38784             }]
38785         }, true);
38786         btn.addClassOnOver("roo-layout-tools-button-over");
38787         return btn;
38788     }
38789 });/*
38790  * Based on:
38791  * Ext JS Library 1.1.1
38792  * Copyright(c) 2006-2007, Ext JS, LLC.
38793  *
38794  * Originally Released Under LGPL - original licence link has changed is not relivant.
38795  *
38796  * Fork - LGPL
38797  * <script type="text/javascript">
38798  */
38799  
38800
38801
38802 /**
38803  * @class Roo.SplitLayoutRegion
38804  * @extends Roo.LayoutRegion
38805  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38806  */
38807 Roo.bootstrap.layout.Split = function(config){
38808     this.cursor = config.cursor;
38809     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38810 };
38811
38812 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38813 {
38814     splitTip : "Drag to resize.",
38815     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38816     useSplitTips : false,
38817
38818     applyConfig : function(config){
38819         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38820     },
38821     
38822     onRender : function(ctr,pos) {
38823         
38824         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38825         if(!this.config.split){
38826             return;
38827         }
38828         if(!this.split){
38829             
38830             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38831                             tag: "div",
38832                             id: this.el.id + "-split",
38833                             cls: "roo-layout-split roo-layout-split-"+this.position,
38834                             html: "&#160;"
38835             });
38836             /** The SplitBar for this region 
38837             * @type Roo.SplitBar */
38838             // does not exist yet...
38839             Roo.log([this.position, this.orientation]);
38840             
38841             this.split = new Roo.bootstrap.SplitBar({
38842                 dragElement : splitEl,
38843                 resizingElement: this.el,
38844                 orientation : this.orientation
38845             });
38846             
38847             this.split.on("moved", this.onSplitMove, this);
38848             this.split.useShim = this.config.useShim === true;
38849             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38850             if(this.useSplitTips){
38851                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38852             }
38853             //if(config.collapsible){
38854             //    this.split.el.on("dblclick", this.collapse,  this);
38855             //}
38856         }
38857         if(typeof this.config.minSize != "undefined"){
38858             this.split.minSize = this.config.minSize;
38859         }
38860         if(typeof this.config.maxSize != "undefined"){
38861             this.split.maxSize = this.config.maxSize;
38862         }
38863         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38864             this.hideSplitter();
38865         }
38866         
38867     },
38868
38869     getHMaxSize : function(){
38870          var cmax = this.config.maxSize || 10000;
38871          var center = this.mgr.getRegion("center");
38872          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38873     },
38874
38875     getVMaxSize : function(){
38876          var cmax = this.config.maxSize || 10000;
38877          var center = this.mgr.getRegion("center");
38878          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38879     },
38880
38881     onSplitMove : function(split, newSize){
38882         this.fireEvent("resized", this, newSize);
38883     },
38884     
38885     /** 
38886      * Returns the {@link Roo.SplitBar} for this region.
38887      * @return {Roo.SplitBar}
38888      */
38889     getSplitBar : function(){
38890         return this.split;
38891     },
38892     
38893     hide : function(){
38894         this.hideSplitter();
38895         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38896     },
38897
38898     hideSplitter : function(){
38899         if(this.split){
38900             this.split.el.setLocation(-2000,-2000);
38901             this.split.el.hide();
38902         }
38903     },
38904
38905     show : function(){
38906         if(this.split){
38907             this.split.el.show();
38908         }
38909         Roo.bootstrap.layout.Split.superclass.show.call(this);
38910     },
38911     
38912     beforeSlide: function(){
38913         if(Roo.isGecko){// firefox overflow auto bug workaround
38914             this.bodyEl.clip();
38915             if(this.tabs) {
38916                 this.tabs.bodyEl.clip();
38917             }
38918             if(this.activePanel){
38919                 this.activePanel.getEl().clip();
38920                 
38921                 if(this.activePanel.beforeSlide){
38922                     this.activePanel.beforeSlide();
38923                 }
38924             }
38925         }
38926     },
38927     
38928     afterSlide : function(){
38929         if(Roo.isGecko){// firefox overflow auto bug workaround
38930             this.bodyEl.unclip();
38931             if(this.tabs) {
38932                 this.tabs.bodyEl.unclip();
38933             }
38934             if(this.activePanel){
38935                 this.activePanel.getEl().unclip();
38936                 if(this.activePanel.afterSlide){
38937                     this.activePanel.afterSlide();
38938                 }
38939             }
38940         }
38941     },
38942
38943     initAutoHide : function(){
38944         if(this.autoHide !== false){
38945             if(!this.autoHideHd){
38946                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38947                 this.autoHideHd = {
38948                     "mouseout": function(e){
38949                         if(!e.within(this.el, true)){
38950                             st.delay(500);
38951                         }
38952                     },
38953                     "mouseover" : function(e){
38954                         st.cancel();
38955                     },
38956                     scope : this
38957                 };
38958             }
38959             this.el.on(this.autoHideHd);
38960         }
38961     },
38962
38963     clearAutoHide : function(){
38964         if(this.autoHide !== false){
38965             this.el.un("mouseout", this.autoHideHd.mouseout);
38966             this.el.un("mouseover", this.autoHideHd.mouseover);
38967         }
38968     },
38969
38970     clearMonitor : function(){
38971         Roo.get(document).un("click", this.slideInIf, this);
38972     },
38973
38974     // these names are backwards but not changed for compat
38975     slideOut : function(){
38976         if(this.isSlid || this.el.hasActiveFx()){
38977             return;
38978         }
38979         this.isSlid = true;
38980         if(this.collapseBtn){
38981             this.collapseBtn.hide();
38982         }
38983         this.closeBtnState = this.closeBtn.getStyle('display');
38984         this.closeBtn.hide();
38985         if(this.stickBtn){
38986             this.stickBtn.show();
38987         }
38988         this.el.show();
38989         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38990         this.beforeSlide();
38991         this.el.setStyle("z-index", 10001);
38992         this.el.slideIn(this.getSlideAnchor(), {
38993             callback: function(){
38994                 this.afterSlide();
38995                 this.initAutoHide();
38996                 Roo.get(document).on("click", this.slideInIf, this);
38997                 this.fireEvent("slideshow", this);
38998             },
38999             scope: this,
39000             block: true
39001         });
39002     },
39003
39004     afterSlideIn : function(){
39005         this.clearAutoHide();
39006         this.isSlid = false;
39007         this.clearMonitor();
39008         this.el.setStyle("z-index", "");
39009         if(this.collapseBtn){
39010             this.collapseBtn.show();
39011         }
39012         this.closeBtn.setStyle('display', this.closeBtnState);
39013         if(this.stickBtn){
39014             this.stickBtn.hide();
39015         }
39016         this.fireEvent("slidehide", this);
39017     },
39018
39019     slideIn : function(cb){
39020         if(!this.isSlid || this.el.hasActiveFx()){
39021             Roo.callback(cb);
39022             return;
39023         }
39024         this.isSlid = false;
39025         this.beforeSlide();
39026         this.el.slideOut(this.getSlideAnchor(), {
39027             callback: function(){
39028                 this.el.setLeftTop(-10000, -10000);
39029                 this.afterSlide();
39030                 this.afterSlideIn();
39031                 Roo.callback(cb);
39032             },
39033             scope: this,
39034             block: true
39035         });
39036     },
39037     
39038     slideInIf : function(e){
39039         if(!e.within(this.el)){
39040             this.slideIn();
39041         }
39042     },
39043
39044     animateCollapse : function(){
39045         this.beforeSlide();
39046         this.el.setStyle("z-index", 20000);
39047         var anchor = this.getSlideAnchor();
39048         this.el.slideOut(anchor, {
39049             callback : function(){
39050                 this.el.setStyle("z-index", "");
39051                 this.collapsedEl.slideIn(anchor, {duration:.3});
39052                 this.afterSlide();
39053                 this.el.setLocation(-10000,-10000);
39054                 this.el.hide();
39055                 this.fireEvent("collapsed", this);
39056             },
39057             scope: this,
39058             block: true
39059         });
39060     },
39061
39062     animateExpand : function(){
39063         this.beforeSlide();
39064         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39065         this.el.setStyle("z-index", 20000);
39066         this.collapsedEl.hide({
39067             duration:.1
39068         });
39069         this.el.slideIn(this.getSlideAnchor(), {
39070             callback : function(){
39071                 this.el.setStyle("z-index", "");
39072                 this.afterSlide();
39073                 if(this.split){
39074                     this.split.el.show();
39075                 }
39076                 this.fireEvent("invalidated", this);
39077                 this.fireEvent("expanded", this);
39078             },
39079             scope: this,
39080             block: true
39081         });
39082     },
39083
39084     anchors : {
39085         "west" : "left",
39086         "east" : "right",
39087         "north" : "top",
39088         "south" : "bottom"
39089     },
39090
39091     sanchors : {
39092         "west" : "l",
39093         "east" : "r",
39094         "north" : "t",
39095         "south" : "b"
39096     },
39097
39098     canchors : {
39099         "west" : "tl-tr",
39100         "east" : "tr-tl",
39101         "north" : "tl-bl",
39102         "south" : "bl-tl"
39103     },
39104
39105     getAnchor : function(){
39106         return this.anchors[this.position];
39107     },
39108
39109     getCollapseAnchor : function(){
39110         return this.canchors[this.position];
39111     },
39112
39113     getSlideAnchor : function(){
39114         return this.sanchors[this.position];
39115     },
39116
39117     getAlignAdj : function(){
39118         var cm = this.cmargins;
39119         switch(this.position){
39120             case "west":
39121                 return [0, 0];
39122             break;
39123             case "east":
39124                 return [0, 0];
39125             break;
39126             case "north":
39127                 return [0, 0];
39128             break;
39129             case "south":
39130                 return [0, 0];
39131             break;
39132         }
39133     },
39134
39135     getExpandAdj : function(){
39136         var c = this.collapsedEl, cm = this.cmargins;
39137         switch(this.position){
39138             case "west":
39139                 return [-(cm.right+c.getWidth()+cm.left), 0];
39140             break;
39141             case "east":
39142                 return [cm.right+c.getWidth()+cm.left, 0];
39143             break;
39144             case "north":
39145                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39146             break;
39147             case "south":
39148                 return [0, cm.top+cm.bottom+c.getHeight()];
39149             break;
39150         }
39151     }
39152 });/*
39153  * Based on:
39154  * Ext JS Library 1.1.1
39155  * Copyright(c) 2006-2007, Ext JS, LLC.
39156  *
39157  * Originally Released Under LGPL - original licence link has changed is not relivant.
39158  *
39159  * Fork - LGPL
39160  * <script type="text/javascript">
39161  */
39162 /*
39163  * These classes are private internal classes
39164  */
39165 Roo.bootstrap.layout.Center = function(config){
39166     config.region = "center";
39167     Roo.bootstrap.layout.Region.call(this, config);
39168     this.visible = true;
39169     this.minWidth = config.minWidth || 20;
39170     this.minHeight = config.minHeight || 20;
39171 };
39172
39173 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39174     hide : function(){
39175         // center panel can't be hidden
39176     },
39177     
39178     show : function(){
39179         // center panel can't be hidden
39180     },
39181     
39182     getMinWidth: function(){
39183         return this.minWidth;
39184     },
39185     
39186     getMinHeight: function(){
39187         return this.minHeight;
39188     }
39189 });
39190
39191
39192
39193
39194  
39195
39196
39197
39198
39199
39200
39201 Roo.bootstrap.layout.North = function(config)
39202 {
39203     config.region = 'north';
39204     config.cursor = 'n-resize';
39205     
39206     Roo.bootstrap.layout.Split.call(this, config);
39207     
39208     
39209     if(this.split){
39210         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39211         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39212         this.split.el.addClass("roo-layout-split-v");
39213     }
39214     var size = config.initialSize || config.height;
39215     if(typeof size != "undefined"){
39216         this.el.setHeight(size);
39217     }
39218 };
39219 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39220 {
39221     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39222     
39223     
39224     
39225     getBox : function(){
39226         if(this.collapsed){
39227             return this.collapsedEl.getBox();
39228         }
39229         var box = this.el.getBox();
39230         if(this.split){
39231             box.height += this.split.el.getHeight();
39232         }
39233         return box;
39234     },
39235     
39236     updateBox : function(box){
39237         if(this.split && !this.collapsed){
39238             box.height -= this.split.el.getHeight();
39239             this.split.el.setLeft(box.x);
39240             this.split.el.setTop(box.y+box.height);
39241             this.split.el.setWidth(box.width);
39242         }
39243         if(this.collapsed){
39244             this.updateBody(box.width, null);
39245         }
39246         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39247     }
39248 });
39249
39250
39251
39252
39253
39254 Roo.bootstrap.layout.South = function(config){
39255     config.region = 'south';
39256     config.cursor = 's-resize';
39257     Roo.bootstrap.layout.Split.call(this, config);
39258     if(this.split){
39259         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39260         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39261         this.split.el.addClass("roo-layout-split-v");
39262     }
39263     var size = config.initialSize || config.height;
39264     if(typeof size != "undefined"){
39265         this.el.setHeight(size);
39266     }
39267 };
39268
39269 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39270     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39271     getBox : function(){
39272         if(this.collapsed){
39273             return this.collapsedEl.getBox();
39274         }
39275         var box = this.el.getBox();
39276         if(this.split){
39277             var sh = this.split.el.getHeight();
39278             box.height += sh;
39279             box.y -= sh;
39280         }
39281         return box;
39282     },
39283     
39284     updateBox : function(box){
39285         if(this.split && !this.collapsed){
39286             var sh = this.split.el.getHeight();
39287             box.height -= sh;
39288             box.y += sh;
39289             this.split.el.setLeft(box.x);
39290             this.split.el.setTop(box.y-sh);
39291             this.split.el.setWidth(box.width);
39292         }
39293         if(this.collapsed){
39294             this.updateBody(box.width, null);
39295         }
39296         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39297     }
39298 });
39299
39300 Roo.bootstrap.layout.East = function(config){
39301     config.region = "east";
39302     config.cursor = "e-resize";
39303     Roo.bootstrap.layout.Split.call(this, config);
39304     if(this.split){
39305         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39306         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39307         this.split.el.addClass("roo-layout-split-h");
39308     }
39309     var size = config.initialSize || config.width;
39310     if(typeof size != "undefined"){
39311         this.el.setWidth(size);
39312     }
39313 };
39314 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39315     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39316     getBox : function(){
39317         if(this.collapsed){
39318             return this.collapsedEl.getBox();
39319         }
39320         var box = this.el.getBox();
39321         if(this.split){
39322             var sw = this.split.el.getWidth();
39323             box.width += sw;
39324             box.x -= sw;
39325         }
39326         return box;
39327     },
39328
39329     updateBox : function(box){
39330         if(this.split && !this.collapsed){
39331             var sw = this.split.el.getWidth();
39332             box.width -= sw;
39333             this.split.el.setLeft(box.x);
39334             this.split.el.setTop(box.y);
39335             this.split.el.setHeight(box.height);
39336             box.x += sw;
39337         }
39338         if(this.collapsed){
39339             this.updateBody(null, box.height);
39340         }
39341         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39342     }
39343 });
39344
39345 Roo.bootstrap.layout.West = function(config){
39346     config.region = "west";
39347     config.cursor = "w-resize";
39348     
39349     Roo.bootstrap.layout.Split.call(this, config);
39350     if(this.split){
39351         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39352         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39353         this.split.el.addClass("roo-layout-split-h");
39354     }
39355     
39356 };
39357 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39358     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39359     
39360     onRender: function(ctr, pos)
39361     {
39362         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39363         var size = this.config.initialSize || this.config.width;
39364         if(typeof size != "undefined"){
39365             this.el.setWidth(size);
39366         }
39367     },
39368     
39369     getBox : function(){
39370         if(this.collapsed){
39371             return this.collapsedEl.getBox();
39372         }
39373         var box = this.el.getBox();
39374         if(this.split){
39375             box.width += this.split.el.getWidth();
39376         }
39377         return box;
39378     },
39379     
39380     updateBox : function(box){
39381         if(this.split && !this.collapsed){
39382             var sw = this.split.el.getWidth();
39383             box.width -= sw;
39384             this.split.el.setLeft(box.x+box.width);
39385             this.split.el.setTop(box.y);
39386             this.split.el.setHeight(box.height);
39387         }
39388         if(this.collapsed){
39389             this.updateBody(null, box.height);
39390         }
39391         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39392     }
39393 });Roo.namespace("Roo.bootstrap.panel");/*
39394  * Based on:
39395  * Ext JS Library 1.1.1
39396  * Copyright(c) 2006-2007, Ext JS, LLC.
39397  *
39398  * Originally Released Under LGPL - original licence link has changed is not relivant.
39399  *
39400  * Fork - LGPL
39401  * <script type="text/javascript">
39402  */
39403 /**
39404  * @class Roo.ContentPanel
39405  * @extends Roo.util.Observable
39406  * A basic ContentPanel element.
39407  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39408  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39409  * @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
39410  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39411  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39412  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39413  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39414  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39415  * @cfg {String} title          The title for this panel
39416  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39417  * @cfg {String} url            Calls {@link #setUrl} with this value
39418  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39419  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39420  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39421  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39422  * @cfg {Boolean} badges render the badges
39423  * @cfg {String} cls  extra classes to use  
39424  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39425
39426  * @constructor
39427  * Create a new ContentPanel.
39428  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39429  * @param {String/Object} config A string to set only the title or a config object
39430  * @param {String} content (optional) Set the HTML content for this panel
39431  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39432  */
39433 Roo.bootstrap.panel.Content = function( config){
39434     
39435     this.tpl = config.tpl || false;
39436     
39437     var el = config.el;
39438     var content = config.content;
39439
39440     if(config.autoCreate){ // xtype is available if this is called from factory
39441         el = Roo.id();
39442     }
39443     this.el = Roo.get(el);
39444     if(!this.el && config && config.autoCreate){
39445         if(typeof config.autoCreate == "object"){
39446             if(!config.autoCreate.id){
39447                 config.autoCreate.id = config.id||el;
39448             }
39449             this.el = Roo.DomHelper.append(document.body,
39450                         config.autoCreate, true);
39451         }else{
39452             var elcfg =  {
39453                 tag: "div",
39454                 cls: (config.cls || '') +
39455                     (config.background ? ' bg-' + config.background : '') +
39456                     " roo-layout-inactive-content",
39457                 id: config.id||el
39458             };
39459             if (config.html) {
39460                 elcfg.html = config.html;
39461                 
39462             }
39463                         
39464             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39465         }
39466     } 
39467     this.closable = false;
39468     this.loaded = false;
39469     this.active = false;
39470    
39471       
39472     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39473         
39474         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39475         
39476         this.wrapEl = this.el; //this.el.wrap();
39477         var ti = [];
39478         if (config.toolbar.items) {
39479             ti = config.toolbar.items ;
39480             delete config.toolbar.items ;
39481         }
39482         
39483         var nitems = [];
39484         this.toolbar.render(this.wrapEl, 'before');
39485         for(var i =0;i < ti.length;i++) {
39486           //  Roo.log(['add child', items[i]]);
39487             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39488         }
39489         this.toolbar.items = nitems;
39490         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39491         delete config.toolbar;
39492         
39493     }
39494     /*
39495     // xtype created footer. - not sure if will work as we normally have to render first..
39496     if (this.footer && !this.footer.el && this.footer.xtype) {
39497         if (!this.wrapEl) {
39498             this.wrapEl = this.el.wrap();
39499         }
39500     
39501         this.footer.container = this.wrapEl.createChild();
39502          
39503         this.footer = Roo.factory(this.footer, Roo);
39504         
39505     }
39506     */
39507     
39508      if(typeof config == "string"){
39509         this.title = config;
39510     }else{
39511         Roo.apply(this, config);
39512     }
39513     
39514     if(this.resizeEl){
39515         this.resizeEl = Roo.get(this.resizeEl, true);
39516     }else{
39517         this.resizeEl = this.el;
39518     }
39519     // handle view.xtype
39520     
39521  
39522     
39523     
39524     this.addEvents({
39525         /**
39526          * @event activate
39527          * Fires when this panel is activated. 
39528          * @param {Roo.ContentPanel} this
39529          */
39530         "activate" : true,
39531         /**
39532          * @event deactivate
39533          * Fires when this panel is activated. 
39534          * @param {Roo.ContentPanel} this
39535          */
39536         "deactivate" : true,
39537
39538         /**
39539          * @event resize
39540          * Fires when this panel is resized if fitToFrame is true.
39541          * @param {Roo.ContentPanel} this
39542          * @param {Number} width The width after any component adjustments
39543          * @param {Number} height The height after any component adjustments
39544          */
39545         "resize" : true,
39546         
39547          /**
39548          * @event render
39549          * Fires when this tab is created
39550          * @param {Roo.ContentPanel} this
39551          */
39552         "render" : true
39553         
39554         
39555         
39556     });
39557     
39558
39559     
39560     
39561     if(this.autoScroll){
39562         this.resizeEl.setStyle("overflow", "auto");
39563     } else {
39564         // fix randome scrolling
39565         //this.el.on('scroll', function() {
39566         //    Roo.log('fix random scolling');
39567         //    this.scrollTo('top',0); 
39568         //});
39569     }
39570     content = content || this.content;
39571     if(content){
39572         this.setContent(content);
39573     }
39574     if(config && config.url){
39575         this.setUrl(this.url, this.params, this.loadOnce);
39576     }
39577     
39578     
39579     
39580     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39581     
39582     if (this.view && typeof(this.view.xtype) != 'undefined') {
39583         this.view.el = this.el.appendChild(document.createElement("div"));
39584         this.view = Roo.factory(this.view); 
39585         this.view.render  &&  this.view.render(false, '');  
39586     }
39587     
39588     
39589     this.fireEvent('render', this);
39590 };
39591
39592 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39593     
39594     cls : '',
39595     background : '',
39596     
39597     tabTip : '',
39598     
39599     setRegion : function(region){
39600         this.region = region;
39601         this.setActiveClass(region && !this.background);
39602     },
39603     
39604     
39605     setActiveClass: function(state)
39606     {
39607         if(state){
39608            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39609            this.el.setStyle('position','relative');
39610         }else{
39611            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39612            this.el.setStyle('position', 'absolute');
39613         } 
39614     },
39615     
39616     /**
39617      * Returns the toolbar for this Panel if one was configured. 
39618      * @return {Roo.Toolbar} 
39619      */
39620     getToolbar : function(){
39621         return this.toolbar;
39622     },
39623     
39624     setActiveState : function(active)
39625     {
39626         this.active = active;
39627         this.setActiveClass(active);
39628         if(!active){
39629             if(this.fireEvent("deactivate", this) === false){
39630                 return false;
39631             }
39632             return true;
39633         }
39634         this.fireEvent("activate", this);
39635         return true;
39636     },
39637     /**
39638      * Updates this panel's element
39639      * @param {String} content The new content
39640      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39641     */
39642     setContent : function(content, loadScripts){
39643         this.el.update(content, loadScripts);
39644     },
39645
39646     ignoreResize : function(w, h){
39647         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39648             return true;
39649         }else{
39650             this.lastSize = {width: w, height: h};
39651             return false;
39652         }
39653     },
39654     /**
39655      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39656      * @return {Roo.UpdateManager} The UpdateManager
39657      */
39658     getUpdateManager : function(){
39659         return this.el.getUpdateManager();
39660     },
39661      /**
39662      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39663      * @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:
39664 <pre><code>
39665 panel.load({
39666     url: "your-url.php",
39667     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39668     callback: yourFunction,
39669     scope: yourObject, //(optional scope)
39670     discardUrl: false,
39671     nocache: false,
39672     text: "Loading...",
39673     timeout: 30,
39674     scripts: false
39675 });
39676 </code></pre>
39677      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39678      * 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.
39679      * @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}
39680      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39681      * @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.
39682      * @return {Roo.ContentPanel} this
39683      */
39684     load : function(){
39685         var um = this.el.getUpdateManager();
39686         um.update.apply(um, arguments);
39687         return this;
39688     },
39689
39690
39691     /**
39692      * 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.
39693      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39694      * @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)
39695      * @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)
39696      * @return {Roo.UpdateManager} The UpdateManager
39697      */
39698     setUrl : function(url, params, loadOnce){
39699         if(this.refreshDelegate){
39700             this.removeListener("activate", this.refreshDelegate);
39701         }
39702         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39703         this.on("activate", this.refreshDelegate);
39704         return this.el.getUpdateManager();
39705     },
39706     
39707     _handleRefresh : function(url, params, loadOnce){
39708         if(!loadOnce || !this.loaded){
39709             var updater = this.el.getUpdateManager();
39710             updater.update(url, params, this._setLoaded.createDelegate(this));
39711         }
39712     },
39713     
39714     _setLoaded : function(){
39715         this.loaded = true;
39716     }, 
39717     
39718     /**
39719      * Returns this panel's id
39720      * @return {String} 
39721      */
39722     getId : function(){
39723         return this.el.id;
39724     },
39725     
39726     /** 
39727      * Returns this panel's element - used by regiosn to add.
39728      * @return {Roo.Element} 
39729      */
39730     getEl : function(){
39731         return this.wrapEl || this.el;
39732     },
39733     
39734    
39735     
39736     adjustForComponents : function(width, height)
39737     {
39738         //Roo.log('adjustForComponents ');
39739         if(this.resizeEl != this.el){
39740             width -= this.el.getFrameWidth('lr');
39741             height -= this.el.getFrameWidth('tb');
39742         }
39743         if(this.toolbar){
39744             var te = this.toolbar.getEl();
39745             te.setWidth(width);
39746             height -= te.getHeight();
39747         }
39748         if(this.footer){
39749             var te = this.footer.getEl();
39750             te.setWidth(width);
39751             height -= te.getHeight();
39752         }
39753         
39754         
39755         if(this.adjustments){
39756             width += this.adjustments[0];
39757             height += this.adjustments[1];
39758         }
39759         return {"width": width, "height": height};
39760     },
39761     
39762     setSize : function(width, height){
39763         if(this.fitToFrame && !this.ignoreResize(width, height)){
39764             if(this.fitContainer && this.resizeEl != this.el){
39765                 this.el.setSize(width, height);
39766             }
39767             var size = this.adjustForComponents(width, height);
39768             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39769             this.fireEvent('resize', this, size.width, size.height);
39770         }
39771     },
39772     
39773     /**
39774      * Returns this panel's title
39775      * @return {String} 
39776      */
39777     getTitle : function(){
39778         
39779         if (typeof(this.title) != 'object') {
39780             return this.title;
39781         }
39782         
39783         var t = '';
39784         for (var k in this.title) {
39785             if (!this.title.hasOwnProperty(k)) {
39786                 continue;
39787             }
39788             
39789             if (k.indexOf('-') >= 0) {
39790                 var s = k.split('-');
39791                 for (var i = 0; i<s.length; i++) {
39792                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39793                 }
39794             } else {
39795                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39796             }
39797         }
39798         return t;
39799     },
39800     
39801     /**
39802      * Set this panel's title
39803      * @param {String} title
39804      */
39805     setTitle : function(title){
39806         this.title = title;
39807         if(this.region){
39808             this.region.updatePanelTitle(this, title);
39809         }
39810     },
39811     
39812     /**
39813      * Returns true is this panel was configured to be closable
39814      * @return {Boolean} 
39815      */
39816     isClosable : function(){
39817         return this.closable;
39818     },
39819     
39820     beforeSlide : function(){
39821         this.el.clip();
39822         this.resizeEl.clip();
39823     },
39824     
39825     afterSlide : function(){
39826         this.el.unclip();
39827         this.resizeEl.unclip();
39828     },
39829     
39830     /**
39831      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39832      *   Will fail silently if the {@link #setUrl} method has not been called.
39833      *   This does not activate the panel, just updates its content.
39834      */
39835     refresh : function(){
39836         if(this.refreshDelegate){
39837            this.loaded = false;
39838            this.refreshDelegate();
39839         }
39840     },
39841     
39842     /**
39843      * Destroys this panel
39844      */
39845     destroy : function(){
39846         this.el.removeAllListeners();
39847         var tempEl = document.createElement("span");
39848         tempEl.appendChild(this.el.dom);
39849         tempEl.innerHTML = "";
39850         this.el.remove();
39851         this.el = null;
39852     },
39853     
39854     /**
39855      * form - if the content panel contains a form - this is a reference to it.
39856      * @type {Roo.form.Form}
39857      */
39858     form : false,
39859     /**
39860      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39861      *    This contains a reference to it.
39862      * @type {Roo.View}
39863      */
39864     view : false,
39865     
39866       /**
39867      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39868      * <pre><code>
39869
39870 layout.addxtype({
39871        xtype : 'Form',
39872        items: [ .... ]
39873    }
39874 );
39875
39876 </code></pre>
39877      * @param {Object} cfg Xtype definition of item to add.
39878      */
39879     
39880     
39881     getChildContainer: function () {
39882         return this.getEl();
39883     }
39884     
39885     
39886     /*
39887         var  ret = new Roo.factory(cfg);
39888         return ret;
39889         
39890         
39891         // add form..
39892         if (cfg.xtype.match(/^Form$/)) {
39893             
39894             var el;
39895             //if (this.footer) {
39896             //    el = this.footer.container.insertSibling(false, 'before');
39897             //} else {
39898                 el = this.el.createChild();
39899             //}
39900
39901             this.form = new  Roo.form.Form(cfg);
39902             
39903             
39904             if ( this.form.allItems.length) {
39905                 this.form.render(el.dom);
39906             }
39907             return this.form;
39908         }
39909         // should only have one of theses..
39910         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39911             // views.. should not be just added - used named prop 'view''
39912             
39913             cfg.el = this.el.appendChild(document.createElement("div"));
39914             // factory?
39915             
39916             var ret = new Roo.factory(cfg);
39917              
39918              ret.render && ret.render(false, ''); // render blank..
39919             this.view = ret;
39920             return ret;
39921         }
39922         return false;
39923     }
39924     \*/
39925 });
39926  
39927 /**
39928  * @class Roo.bootstrap.panel.Grid
39929  * @extends Roo.bootstrap.panel.Content
39930  * @constructor
39931  * Create a new GridPanel.
39932  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39933  * @param {Object} config A the config object
39934   
39935  */
39936
39937
39938
39939 Roo.bootstrap.panel.Grid = function(config)
39940 {
39941     
39942       
39943     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39944         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39945
39946     config.el = this.wrapper;
39947     //this.el = this.wrapper;
39948     
39949       if (config.container) {
39950         // ctor'ed from a Border/panel.grid
39951         
39952         
39953         this.wrapper.setStyle("overflow", "hidden");
39954         this.wrapper.addClass('roo-grid-container');
39955
39956     }
39957     
39958     
39959     if(config.toolbar){
39960         var tool_el = this.wrapper.createChild();    
39961         this.toolbar = Roo.factory(config.toolbar);
39962         var ti = [];
39963         if (config.toolbar.items) {
39964             ti = config.toolbar.items ;
39965             delete config.toolbar.items ;
39966         }
39967         
39968         var nitems = [];
39969         this.toolbar.render(tool_el);
39970         for(var i =0;i < ti.length;i++) {
39971           //  Roo.log(['add child', items[i]]);
39972             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39973         }
39974         this.toolbar.items = nitems;
39975         
39976         delete config.toolbar;
39977     }
39978     
39979     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39980     config.grid.scrollBody = true;;
39981     config.grid.monitorWindowResize = false; // turn off autosizing
39982     config.grid.autoHeight = false;
39983     config.grid.autoWidth = false;
39984     
39985     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39986     
39987     if (config.background) {
39988         // render grid on panel activation (if panel background)
39989         this.on('activate', function(gp) {
39990             if (!gp.grid.rendered) {
39991                 gp.grid.render(this.wrapper);
39992                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39993             }
39994         });
39995             
39996     } else {
39997         this.grid.render(this.wrapper);
39998         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
39999
40000     }
40001     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40002     // ??? needed ??? config.el = this.wrapper;
40003     
40004     
40005     
40006   
40007     // xtype created footer. - not sure if will work as we normally have to render first..
40008     if (this.footer && !this.footer.el && this.footer.xtype) {
40009         
40010         var ctr = this.grid.getView().getFooterPanel(true);
40011         this.footer.dataSource = this.grid.dataSource;
40012         this.footer = Roo.factory(this.footer, Roo);
40013         this.footer.render(ctr);
40014         
40015     }
40016     
40017     
40018     
40019     
40020      
40021 };
40022
40023 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40024     getId : function(){
40025         return this.grid.id;
40026     },
40027     
40028     /**
40029      * Returns the grid for this panel
40030      * @return {Roo.bootstrap.Table} 
40031      */
40032     getGrid : function(){
40033         return this.grid;    
40034     },
40035     
40036     setSize : function(width, height){
40037         if(!this.ignoreResize(width, height)){
40038             var grid = this.grid;
40039             var size = this.adjustForComponents(width, height);
40040             // tfoot is not a footer?
40041           
40042             
40043             var gridel = grid.getGridEl();
40044             gridel.setSize(size.width, size.height);
40045             
40046             var tbd = grid.getGridEl().select('tbody', true).first();
40047             var thd = grid.getGridEl().select('thead',true).first();
40048             var tbf= grid.getGridEl().select('tfoot', true).first();
40049
40050             if (tbf) {
40051                 size.height -= thd.getHeight();
40052             }
40053             if (thd) {
40054                 size.height -= thd.getHeight();
40055             }
40056             
40057             tbd.setSize(size.width, size.height );
40058             // this is for the account management tab -seems to work there.
40059             var thd = grid.getGridEl().select('thead',true).first();
40060             //if (tbd) {
40061             //    tbd.setSize(size.width, size.height - thd.getHeight());
40062             //}
40063              
40064             grid.autoSize();
40065         }
40066     },
40067      
40068     
40069     
40070     beforeSlide : function(){
40071         this.grid.getView().scroller.clip();
40072     },
40073     
40074     afterSlide : function(){
40075         this.grid.getView().scroller.unclip();
40076     },
40077     
40078     destroy : function(){
40079         this.grid.destroy();
40080         delete this.grid;
40081         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40082     }
40083 });
40084
40085 /**
40086  * @class Roo.bootstrap.panel.Nest
40087  * @extends Roo.bootstrap.panel.Content
40088  * @constructor
40089  * Create a new Panel, that can contain a layout.Border.
40090  * 
40091  * 
40092  * @param {Roo.BorderLayout} layout The layout for this panel
40093  * @param {String/Object} config A string to set only the title or a config object
40094  */
40095 Roo.bootstrap.panel.Nest = function(config)
40096 {
40097     // construct with only one argument..
40098     /* FIXME - implement nicer consturctors
40099     if (layout.layout) {
40100         config = layout;
40101         layout = config.layout;
40102         delete config.layout;
40103     }
40104     if (layout.xtype && !layout.getEl) {
40105         // then layout needs constructing..
40106         layout = Roo.factory(layout, Roo);
40107     }
40108     */
40109     
40110     config.el =  config.layout.getEl();
40111     
40112     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40113     
40114     config.layout.monitorWindowResize = false; // turn off autosizing
40115     this.layout = config.layout;
40116     this.layout.getEl().addClass("roo-layout-nested-layout");
40117     this.layout.parent = this;
40118     
40119     
40120     
40121     
40122 };
40123
40124 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40125
40126     setSize : function(width, height){
40127         if(!this.ignoreResize(width, height)){
40128             var size = this.adjustForComponents(width, height);
40129             var el = this.layout.getEl();
40130             if (size.height < 1) {
40131                 el.setWidth(size.width);   
40132             } else {
40133                 el.setSize(size.width, size.height);
40134             }
40135             var touch = el.dom.offsetWidth;
40136             this.layout.layout();
40137             // ie requires a double layout on the first pass
40138             if(Roo.isIE && !this.initialized){
40139                 this.initialized = true;
40140                 this.layout.layout();
40141             }
40142         }
40143     },
40144     
40145     // activate all subpanels if not currently active..
40146     
40147     setActiveState : function(active){
40148         this.active = active;
40149         this.setActiveClass(active);
40150         
40151         if(!active){
40152             this.fireEvent("deactivate", this);
40153             return;
40154         }
40155         
40156         this.fireEvent("activate", this);
40157         // not sure if this should happen before or after..
40158         if (!this.layout) {
40159             return; // should not happen..
40160         }
40161         var reg = false;
40162         for (var r in this.layout.regions) {
40163             reg = this.layout.getRegion(r);
40164             if (reg.getActivePanel()) {
40165                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40166                 reg.setActivePanel(reg.getActivePanel());
40167                 continue;
40168             }
40169             if (!reg.panels.length) {
40170                 continue;
40171             }
40172             reg.showPanel(reg.getPanel(0));
40173         }
40174         
40175         
40176         
40177         
40178     },
40179     
40180     /**
40181      * Returns the nested BorderLayout for this panel
40182      * @return {Roo.BorderLayout} 
40183      */
40184     getLayout : function(){
40185         return this.layout;
40186     },
40187     
40188      /**
40189      * Adds a xtype elements to the layout of the nested panel
40190      * <pre><code>
40191
40192 panel.addxtype({
40193        xtype : 'ContentPanel',
40194        region: 'west',
40195        items: [ .... ]
40196    }
40197 );
40198
40199 panel.addxtype({
40200         xtype : 'NestedLayoutPanel',
40201         region: 'west',
40202         layout: {
40203            center: { },
40204            west: { }   
40205         },
40206         items : [ ... list of content panels or nested layout panels.. ]
40207    }
40208 );
40209 </code></pre>
40210      * @param {Object} cfg Xtype definition of item to add.
40211      */
40212     addxtype : function(cfg) {
40213         return this.layout.addxtype(cfg);
40214     
40215     }
40216 });/*
40217  * Based on:
40218  * Ext JS Library 1.1.1
40219  * Copyright(c) 2006-2007, Ext JS, LLC.
40220  *
40221  * Originally Released Under LGPL - original licence link has changed is not relivant.
40222  *
40223  * Fork - LGPL
40224  * <script type="text/javascript">
40225  */
40226 /**
40227  * @class Roo.TabPanel
40228  * @extends Roo.util.Observable
40229  * A lightweight tab container.
40230  * <br><br>
40231  * Usage:
40232  * <pre><code>
40233 // basic tabs 1, built from existing content
40234 var tabs = new Roo.TabPanel("tabs1");
40235 tabs.addTab("script", "View Script");
40236 tabs.addTab("markup", "View Markup");
40237 tabs.activate("script");
40238
40239 // more advanced tabs, built from javascript
40240 var jtabs = new Roo.TabPanel("jtabs");
40241 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40242
40243 // set up the UpdateManager
40244 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40245 var updater = tab2.getUpdateManager();
40246 updater.setDefaultUrl("ajax1.htm");
40247 tab2.on('activate', updater.refresh, updater, true);
40248
40249 // Use setUrl for Ajax loading
40250 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40251 tab3.setUrl("ajax2.htm", null, true);
40252
40253 // Disabled tab
40254 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40255 tab4.disable();
40256
40257 jtabs.activate("jtabs-1");
40258  * </code></pre>
40259  * @constructor
40260  * Create a new TabPanel.
40261  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40262  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40263  */
40264 Roo.bootstrap.panel.Tabs = function(config){
40265     /**
40266     * The container element for this TabPanel.
40267     * @type Roo.Element
40268     */
40269     this.el = Roo.get(config.el);
40270     delete config.el;
40271     if(config){
40272         if(typeof config == "boolean"){
40273             this.tabPosition = config ? "bottom" : "top";
40274         }else{
40275             Roo.apply(this, config);
40276         }
40277     }
40278     
40279     if(this.tabPosition == "bottom"){
40280         // if tabs are at the bottom = create the body first.
40281         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40282         this.el.addClass("roo-tabs-bottom");
40283     }
40284     // next create the tabs holders
40285     
40286     if (this.tabPosition == "west"){
40287         
40288         var reg = this.region; // fake it..
40289         while (reg) {
40290             if (!reg.mgr.parent) {
40291                 break;
40292             }
40293             reg = reg.mgr.parent.region;
40294         }
40295         Roo.log("got nest?");
40296         Roo.log(reg);
40297         if (reg.mgr.getRegion('west')) {
40298             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40299             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40300             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40301             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40302             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40303         
40304             
40305         }
40306         
40307         
40308     } else {
40309      
40310         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40311         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40312         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40313         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40314     }
40315     
40316     
40317     if(Roo.isIE){
40318         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40319     }
40320     
40321     // finally - if tabs are at the top, then create the body last..
40322     if(this.tabPosition != "bottom"){
40323         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40324          * @type Roo.Element
40325          */
40326         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40327         this.el.addClass("roo-tabs-top");
40328     }
40329     this.items = [];
40330
40331     this.bodyEl.setStyle("position", "relative");
40332
40333     this.active = null;
40334     this.activateDelegate = this.activate.createDelegate(this);
40335
40336     this.addEvents({
40337         /**
40338          * @event tabchange
40339          * Fires when the active tab changes
40340          * @param {Roo.TabPanel} this
40341          * @param {Roo.TabPanelItem} activePanel The new active tab
40342          */
40343         "tabchange": true,
40344         /**
40345          * @event beforetabchange
40346          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40347          * @param {Roo.TabPanel} this
40348          * @param {Object} e Set cancel to true on this object to cancel the tab change
40349          * @param {Roo.TabPanelItem} tab The tab being changed to
40350          */
40351         "beforetabchange" : true
40352     });
40353
40354     Roo.EventManager.onWindowResize(this.onResize, this);
40355     this.cpad = this.el.getPadding("lr");
40356     this.hiddenCount = 0;
40357
40358
40359     // toolbar on the tabbar support...
40360     if (this.toolbar) {
40361         alert("no toolbar support yet");
40362         this.toolbar  = false;
40363         /*
40364         var tcfg = this.toolbar;
40365         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40366         this.toolbar = new Roo.Toolbar(tcfg);
40367         if (Roo.isSafari) {
40368             var tbl = tcfg.container.child('table', true);
40369             tbl.setAttribute('width', '100%');
40370         }
40371         */
40372         
40373     }
40374    
40375
40376
40377     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40378 };
40379
40380 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40381     /*
40382      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40383      */
40384     tabPosition : "top",
40385     /*
40386      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40387      */
40388     currentTabWidth : 0,
40389     /*
40390      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40391      */
40392     minTabWidth : 40,
40393     /*
40394      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40395      */
40396     maxTabWidth : 250,
40397     /*
40398      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40399      */
40400     preferredTabWidth : 175,
40401     /*
40402      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40403      */
40404     resizeTabs : false,
40405     /*
40406      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40407      */
40408     monitorResize : true,
40409     /*
40410      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40411      */
40412     toolbar : false,  // set by caller..
40413     
40414     region : false, /// set by caller
40415     
40416     disableTooltips : true, // not used yet...
40417
40418     /**
40419      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40420      * @param {String} id The id of the div to use <b>or create</b>
40421      * @param {String} text The text for the tab
40422      * @param {String} content (optional) Content to put in the TabPanelItem body
40423      * @param {Boolean} closable (optional) True to create a close icon on the tab
40424      * @return {Roo.TabPanelItem} The created TabPanelItem
40425      */
40426     addTab : function(id, text, content, closable, tpl)
40427     {
40428         var item = new Roo.bootstrap.panel.TabItem({
40429             panel: this,
40430             id : id,
40431             text : text,
40432             closable : closable,
40433             tpl : tpl
40434         });
40435         this.addTabItem(item);
40436         if(content){
40437             item.setContent(content);
40438         }
40439         return item;
40440     },
40441
40442     /**
40443      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40444      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40445      * @return {Roo.TabPanelItem}
40446      */
40447     getTab : function(id){
40448         return this.items[id];
40449     },
40450
40451     /**
40452      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40453      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40454      */
40455     hideTab : function(id){
40456         var t = this.items[id];
40457         if(!t.isHidden()){
40458            t.setHidden(true);
40459            this.hiddenCount++;
40460            this.autoSizeTabs();
40461         }
40462     },
40463
40464     /**
40465      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40466      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40467      */
40468     unhideTab : function(id){
40469         var t = this.items[id];
40470         if(t.isHidden()){
40471            t.setHidden(false);
40472            this.hiddenCount--;
40473            this.autoSizeTabs();
40474         }
40475     },
40476
40477     /**
40478      * Adds an existing {@link Roo.TabPanelItem}.
40479      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40480      */
40481     addTabItem : function(item)
40482     {
40483         this.items[item.id] = item;
40484         this.items.push(item);
40485         this.autoSizeTabs();
40486       //  if(this.resizeTabs){
40487     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40488   //         this.autoSizeTabs();
40489 //        }else{
40490 //            item.autoSize();
40491        // }
40492     },
40493
40494     /**
40495      * Removes a {@link Roo.TabPanelItem}.
40496      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40497      */
40498     removeTab : function(id){
40499         var items = this.items;
40500         var tab = items[id];
40501         if(!tab) { return; }
40502         var index = items.indexOf(tab);
40503         if(this.active == tab && items.length > 1){
40504             var newTab = this.getNextAvailable(index);
40505             if(newTab) {
40506                 newTab.activate();
40507             }
40508         }
40509         this.stripEl.dom.removeChild(tab.pnode.dom);
40510         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40511             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40512         }
40513         items.splice(index, 1);
40514         delete this.items[tab.id];
40515         tab.fireEvent("close", tab);
40516         tab.purgeListeners();
40517         this.autoSizeTabs();
40518     },
40519
40520     getNextAvailable : function(start){
40521         var items = this.items;
40522         var index = start;
40523         // look for a next tab that will slide over to
40524         // replace the one being removed
40525         while(index < items.length){
40526             var item = items[++index];
40527             if(item && !item.isHidden()){
40528                 return item;
40529             }
40530         }
40531         // if one isn't found select the previous tab (on the left)
40532         index = start;
40533         while(index >= 0){
40534             var item = items[--index];
40535             if(item && !item.isHidden()){
40536                 return item;
40537             }
40538         }
40539         return null;
40540     },
40541
40542     /**
40543      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40544      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40545      */
40546     disableTab : function(id){
40547         var tab = this.items[id];
40548         if(tab && this.active != tab){
40549             tab.disable();
40550         }
40551     },
40552
40553     /**
40554      * Enables a {@link Roo.TabPanelItem} that is disabled.
40555      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40556      */
40557     enableTab : function(id){
40558         var tab = this.items[id];
40559         tab.enable();
40560     },
40561
40562     /**
40563      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40564      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40565      * @return {Roo.TabPanelItem} The TabPanelItem.
40566      */
40567     activate : function(id)
40568     {
40569         //Roo.log('activite:'  + id);
40570         
40571         var tab = this.items[id];
40572         if(!tab){
40573             return null;
40574         }
40575         if(tab == this.active || tab.disabled){
40576             return tab;
40577         }
40578         var e = {};
40579         this.fireEvent("beforetabchange", this, e, tab);
40580         if(e.cancel !== true && !tab.disabled){
40581             if(this.active){
40582                 this.active.hide();
40583             }
40584             this.active = this.items[id];
40585             this.active.show();
40586             this.fireEvent("tabchange", this, this.active);
40587         }
40588         return tab;
40589     },
40590
40591     /**
40592      * Gets the active {@link Roo.TabPanelItem}.
40593      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40594      */
40595     getActiveTab : function(){
40596         return this.active;
40597     },
40598
40599     /**
40600      * Updates the tab body element to fit the height of the container element
40601      * for overflow scrolling
40602      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40603      */
40604     syncHeight : function(targetHeight){
40605         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40606         var bm = this.bodyEl.getMargins();
40607         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40608         this.bodyEl.setHeight(newHeight);
40609         return newHeight;
40610     },
40611
40612     onResize : function(){
40613         if(this.monitorResize){
40614             this.autoSizeTabs();
40615         }
40616     },
40617
40618     /**
40619      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40620      */
40621     beginUpdate : function(){
40622         this.updating = true;
40623     },
40624
40625     /**
40626      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40627      */
40628     endUpdate : function(){
40629         this.updating = false;
40630         this.autoSizeTabs();
40631     },
40632
40633     /**
40634      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40635      */
40636     autoSizeTabs : function()
40637     {
40638         var count = this.items.length;
40639         var vcount = count - this.hiddenCount;
40640         
40641         if (vcount < 2) {
40642             this.stripEl.hide();
40643         } else {
40644             this.stripEl.show();
40645         }
40646         
40647         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40648             return;
40649         }
40650         
40651         
40652         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40653         var availWidth = Math.floor(w / vcount);
40654         var b = this.stripBody;
40655         if(b.getWidth() > w){
40656             var tabs = this.items;
40657             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40658             if(availWidth < this.minTabWidth){
40659                 /*if(!this.sleft){    // incomplete scrolling code
40660                     this.createScrollButtons();
40661                 }
40662                 this.showScroll();
40663                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40664             }
40665         }else{
40666             if(this.currentTabWidth < this.preferredTabWidth){
40667                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40668             }
40669         }
40670     },
40671
40672     /**
40673      * Returns the number of tabs in this TabPanel.
40674      * @return {Number}
40675      */
40676      getCount : function(){
40677          return this.items.length;
40678      },
40679
40680     /**
40681      * Resizes all the tabs to the passed width
40682      * @param {Number} The new width
40683      */
40684     setTabWidth : function(width){
40685         this.currentTabWidth = width;
40686         for(var i = 0, len = this.items.length; i < len; i++) {
40687                 if(!this.items[i].isHidden()) {
40688                 this.items[i].setWidth(width);
40689             }
40690         }
40691     },
40692
40693     /**
40694      * Destroys this TabPanel
40695      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40696      */
40697     destroy : function(removeEl){
40698         Roo.EventManager.removeResizeListener(this.onResize, this);
40699         for(var i = 0, len = this.items.length; i < len; i++){
40700             this.items[i].purgeListeners();
40701         }
40702         if(removeEl === true){
40703             this.el.update("");
40704             this.el.remove();
40705         }
40706     },
40707     
40708     createStrip : function(container)
40709     {
40710         var strip = document.createElement("nav");
40711         strip.className = Roo.bootstrap.version == 4 ?
40712             "navbar-light bg-light" : 
40713             "navbar navbar-default"; //"x-tabs-wrap";
40714         container.appendChild(strip);
40715         return strip;
40716     },
40717     
40718     createStripList : function(strip)
40719     {
40720         // div wrapper for retard IE
40721         // returns the "tr" element.
40722         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40723         //'<div class="x-tabs-strip-wrap">'+
40724           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40725           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40726         return strip.firstChild; //.firstChild.firstChild.firstChild;
40727     },
40728     createBody : function(container)
40729     {
40730         var body = document.createElement("div");
40731         Roo.id(body, "tab-body");
40732         //Roo.fly(body).addClass("x-tabs-body");
40733         Roo.fly(body).addClass("tab-content");
40734         container.appendChild(body);
40735         return body;
40736     },
40737     createItemBody :function(bodyEl, id){
40738         var body = Roo.getDom(id);
40739         if(!body){
40740             body = document.createElement("div");
40741             body.id = id;
40742         }
40743         //Roo.fly(body).addClass("x-tabs-item-body");
40744         Roo.fly(body).addClass("tab-pane");
40745          bodyEl.insertBefore(body, bodyEl.firstChild);
40746         return body;
40747     },
40748     /** @private */
40749     createStripElements :  function(stripEl, text, closable, tpl)
40750     {
40751         var td = document.createElement("li"); // was td..
40752         td.className = 'nav-item';
40753         
40754         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40755         
40756         
40757         stripEl.appendChild(td);
40758         /*if(closable){
40759             td.className = "x-tabs-closable";
40760             if(!this.closeTpl){
40761                 this.closeTpl = new Roo.Template(
40762                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40763                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40764                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40765                 );
40766             }
40767             var el = this.closeTpl.overwrite(td, {"text": text});
40768             var close = el.getElementsByTagName("div")[0];
40769             var inner = el.getElementsByTagName("em")[0];
40770             return {"el": el, "close": close, "inner": inner};
40771         } else {
40772         */
40773         // not sure what this is..
40774 //            if(!this.tabTpl){
40775                 //this.tabTpl = 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></em></span></a>'
40778                 //);
40779 //                this.tabTpl = new Roo.Template(
40780 //                   '<a href="#">' +
40781 //                   '<span unselectable="on"' +
40782 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40783 //                            ' >{text}</span></a>'
40784 //                );
40785 //                
40786 //            }
40787
40788
40789             var template = tpl || this.tabTpl || false;
40790             
40791             if(!template){
40792                 template =  new Roo.Template(
40793                         Roo.bootstrap.version == 4 ? 
40794                             (
40795                                 '<a class="nav-link" href="#" unselectable="on"' +
40796                                      (this.disableTooltips ? '' : ' title="{text}"') +
40797                                      ' >{text}</a>'
40798                             ) : (
40799                                 '<a class="nav-link" href="#">' +
40800                                 '<span unselectable="on"' +
40801                                          (this.disableTooltips ? '' : ' title="{text}"') +
40802                                     ' >{text}</span></a>'
40803                             )
40804                 );
40805             }
40806             
40807             switch (typeof(template)) {
40808                 case 'object' :
40809                     break;
40810                 case 'string' :
40811                     template = new Roo.Template(template);
40812                     break;
40813                 default :
40814                     break;
40815             }
40816             
40817             var el = template.overwrite(td, {"text": text});
40818             
40819             var inner = el.getElementsByTagName("span")[0];
40820             
40821             return {"el": el, "inner": inner};
40822             
40823     }
40824         
40825     
40826 });
40827
40828 /**
40829  * @class Roo.TabPanelItem
40830  * @extends Roo.util.Observable
40831  * Represents an individual item (tab plus body) in a TabPanel.
40832  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40833  * @param {String} id The id of this TabPanelItem
40834  * @param {String} text The text for the tab of this TabPanelItem
40835  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40836  */
40837 Roo.bootstrap.panel.TabItem = function(config){
40838     /**
40839      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40840      * @type Roo.TabPanel
40841      */
40842     this.tabPanel = config.panel;
40843     /**
40844      * The id for this TabPanelItem
40845      * @type String
40846      */
40847     this.id = config.id;
40848     /** @private */
40849     this.disabled = false;
40850     /** @private */
40851     this.text = config.text;
40852     /** @private */
40853     this.loaded = false;
40854     this.closable = config.closable;
40855
40856     /**
40857      * The body element for this TabPanelItem.
40858      * @type Roo.Element
40859      */
40860     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40861     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40862     this.bodyEl.setStyle("display", "block");
40863     this.bodyEl.setStyle("zoom", "1");
40864     //this.hideAction();
40865
40866     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40867     /** @private */
40868     this.el = Roo.get(els.el);
40869     this.inner = Roo.get(els.inner, true);
40870      this.textEl = Roo.bootstrap.version == 4 ?
40871         this.el : Roo.get(this.el.dom.firstChild, true);
40872
40873     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40874     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40875
40876     
40877 //    this.el.on("mousedown", this.onTabMouseDown, this);
40878     this.el.on("click", this.onTabClick, this);
40879     /** @private */
40880     if(config.closable){
40881         var c = Roo.get(els.close, true);
40882         c.dom.title = this.closeText;
40883         c.addClassOnOver("close-over");
40884         c.on("click", this.closeClick, this);
40885      }
40886
40887     this.addEvents({
40888          /**
40889          * @event activate
40890          * Fires when this tab becomes the active tab.
40891          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40892          * @param {Roo.TabPanelItem} this
40893          */
40894         "activate": true,
40895         /**
40896          * @event beforeclose
40897          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40898          * @param {Roo.TabPanelItem} this
40899          * @param {Object} e Set cancel to true on this object to cancel the close.
40900          */
40901         "beforeclose": true,
40902         /**
40903          * @event close
40904          * Fires when this tab is closed.
40905          * @param {Roo.TabPanelItem} this
40906          */
40907          "close": true,
40908         /**
40909          * @event deactivate
40910          * Fires when this tab is no longer the active tab.
40911          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40912          * @param {Roo.TabPanelItem} this
40913          */
40914          "deactivate" : true
40915     });
40916     this.hidden = false;
40917
40918     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40919 };
40920
40921 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40922            {
40923     purgeListeners : function(){
40924        Roo.util.Observable.prototype.purgeListeners.call(this);
40925        this.el.removeAllListeners();
40926     },
40927     /**
40928      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40929      */
40930     show : function(){
40931         this.status_node.addClass("active");
40932         this.showAction();
40933         if(Roo.isOpera){
40934             this.tabPanel.stripWrap.repaint();
40935         }
40936         this.fireEvent("activate", this.tabPanel, this);
40937     },
40938
40939     /**
40940      * Returns true if this tab is the active tab.
40941      * @return {Boolean}
40942      */
40943     isActive : function(){
40944         return this.tabPanel.getActiveTab() == this;
40945     },
40946
40947     /**
40948      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40949      */
40950     hide : function(){
40951         this.status_node.removeClass("active");
40952         this.hideAction();
40953         this.fireEvent("deactivate", this.tabPanel, this);
40954     },
40955
40956     hideAction : function(){
40957         this.bodyEl.hide();
40958         this.bodyEl.setStyle("position", "absolute");
40959         this.bodyEl.setLeft("-20000px");
40960         this.bodyEl.setTop("-20000px");
40961     },
40962
40963     showAction : function(){
40964         this.bodyEl.setStyle("position", "relative");
40965         this.bodyEl.setTop("");
40966         this.bodyEl.setLeft("");
40967         this.bodyEl.show();
40968     },
40969
40970     /**
40971      * Set the tooltip for the tab.
40972      * @param {String} tooltip The tab's tooltip
40973      */
40974     setTooltip : function(text){
40975         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40976             this.textEl.dom.qtip = text;
40977             this.textEl.dom.removeAttribute('title');
40978         }else{
40979             this.textEl.dom.title = text;
40980         }
40981     },
40982
40983     onTabClick : function(e){
40984         e.preventDefault();
40985         this.tabPanel.activate(this.id);
40986     },
40987
40988     onTabMouseDown : function(e){
40989         e.preventDefault();
40990         this.tabPanel.activate(this.id);
40991     },
40992 /*
40993     getWidth : function(){
40994         return this.inner.getWidth();
40995     },
40996
40997     setWidth : function(width){
40998         var iwidth = width - this.linode.getPadding("lr");
40999         this.inner.setWidth(iwidth);
41000         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41001         this.linode.setWidth(width);
41002     },
41003 */
41004     /**
41005      * Show or hide the tab
41006      * @param {Boolean} hidden True to hide or false to show.
41007      */
41008     setHidden : function(hidden){
41009         this.hidden = hidden;
41010         this.linode.setStyle("display", hidden ? "none" : "");
41011     },
41012
41013     /**
41014      * Returns true if this tab is "hidden"
41015      * @return {Boolean}
41016      */
41017     isHidden : function(){
41018         return this.hidden;
41019     },
41020
41021     /**
41022      * Returns the text for this tab
41023      * @return {String}
41024      */
41025     getText : function(){
41026         return this.text;
41027     },
41028     /*
41029     autoSize : function(){
41030         //this.el.beginMeasure();
41031         this.textEl.setWidth(1);
41032         /*
41033          *  #2804 [new] Tabs in Roojs
41034          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41035          */
41036         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41037         //this.el.endMeasure();
41038     //},
41039
41040     /**
41041      * Sets the text for the tab (Note: this also sets the tooltip text)
41042      * @param {String} text The tab's text and tooltip
41043      */
41044     setText : function(text){
41045         this.text = text;
41046         this.textEl.update(text);
41047         this.setTooltip(text);
41048         //if(!this.tabPanel.resizeTabs){
41049         //    this.autoSize();
41050         //}
41051     },
41052     /**
41053      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41054      */
41055     activate : function(){
41056         this.tabPanel.activate(this.id);
41057     },
41058
41059     /**
41060      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41061      */
41062     disable : function(){
41063         if(this.tabPanel.active != this){
41064             this.disabled = true;
41065             this.status_node.addClass("disabled");
41066         }
41067     },
41068
41069     /**
41070      * Enables this TabPanelItem if it was previously disabled.
41071      */
41072     enable : function(){
41073         this.disabled = false;
41074         this.status_node.removeClass("disabled");
41075     },
41076
41077     /**
41078      * Sets the content for this TabPanelItem.
41079      * @param {String} content The content
41080      * @param {Boolean} loadScripts true to look for and load scripts
41081      */
41082     setContent : function(content, loadScripts){
41083         this.bodyEl.update(content, loadScripts);
41084     },
41085
41086     /**
41087      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41088      * @return {Roo.UpdateManager} The UpdateManager
41089      */
41090     getUpdateManager : function(){
41091         return this.bodyEl.getUpdateManager();
41092     },
41093
41094     /**
41095      * Set a URL to be used to load the content for this TabPanelItem.
41096      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41097      * @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)
41098      * @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)
41099      * @return {Roo.UpdateManager} The UpdateManager
41100      */
41101     setUrl : function(url, params, loadOnce){
41102         if(this.refreshDelegate){
41103             this.un('activate', this.refreshDelegate);
41104         }
41105         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41106         this.on("activate", this.refreshDelegate);
41107         return this.bodyEl.getUpdateManager();
41108     },
41109
41110     /** @private */
41111     _handleRefresh : function(url, params, loadOnce){
41112         if(!loadOnce || !this.loaded){
41113             var updater = this.bodyEl.getUpdateManager();
41114             updater.update(url, params, this._setLoaded.createDelegate(this));
41115         }
41116     },
41117
41118     /**
41119      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41120      *   Will fail silently if the setUrl method has not been called.
41121      *   This does not activate the panel, just updates its content.
41122      */
41123     refresh : function(){
41124         if(this.refreshDelegate){
41125            this.loaded = false;
41126            this.refreshDelegate();
41127         }
41128     },
41129
41130     /** @private */
41131     _setLoaded : function(){
41132         this.loaded = true;
41133     },
41134
41135     /** @private */
41136     closeClick : function(e){
41137         var o = {};
41138         e.stopEvent();
41139         this.fireEvent("beforeclose", this, o);
41140         if(o.cancel !== true){
41141             this.tabPanel.removeTab(this.id);
41142         }
41143     },
41144     /**
41145      * The text displayed in the tooltip for the close icon.
41146      * @type String
41147      */
41148     closeText : "Close this tab"
41149 });
41150 /**
41151 *    This script refer to:
41152 *    Title: International Telephone Input
41153 *    Author: Jack O'Connor
41154 *    Code version:  v12.1.12
41155 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41156 **/
41157
41158 Roo.bootstrap.PhoneInputData = function() {
41159     var d = [
41160       [
41161         "Afghanistan (‫افغانستان‬‎)",
41162         "af",
41163         "93"
41164       ],
41165       [
41166         "Albania (Shqipëri)",
41167         "al",
41168         "355"
41169       ],
41170       [
41171         "Algeria (‫الجزائر‬‎)",
41172         "dz",
41173         "213"
41174       ],
41175       [
41176         "American Samoa",
41177         "as",
41178         "1684"
41179       ],
41180       [
41181         "Andorra",
41182         "ad",
41183         "376"
41184       ],
41185       [
41186         "Angola",
41187         "ao",
41188         "244"
41189       ],
41190       [
41191         "Anguilla",
41192         "ai",
41193         "1264"
41194       ],
41195       [
41196         "Antigua and Barbuda",
41197         "ag",
41198         "1268"
41199       ],
41200       [
41201         "Argentina",
41202         "ar",
41203         "54"
41204       ],
41205       [
41206         "Armenia (Հայաստան)",
41207         "am",
41208         "374"
41209       ],
41210       [
41211         "Aruba",
41212         "aw",
41213         "297"
41214       ],
41215       [
41216         "Australia",
41217         "au",
41218         "61",
41219         0
41220       ],
41221       [
41222         "Austria (Österreich)",
41223         "at",
41224         "43"
41225       ],
41226       [
41227         "Azerbaijan (Azərbaycan)",
41228         "az",
41229         "994"
41230       ],
41231       [
41232         "Bahamas",
41233         "bs",
41234         "1242"
41235       ],
41236       [
41237         "Bahrain (‫البحرين‬‎)",
41238         "bh",
41239         "973"
41240       ],
41241       [
41242         "Bangladesh (বাংলাদেশ)",
41243         "bd",
41244         "880"
41245       ],
41246       [
41247         "Barbados",
41248         "bb",
41249         "1246"
41250       ],
41251       [
41252         "Belarus (Беларусь)",
41253         "by",
41254         "375"
41255       ],
41256       [
41257         "Belgium (België)",
41258         "be",
41259         "32"
41260       ],
41261       [
41262         "Belize",
41263         "bz",
41264         "501"
41265       ],
41266       [
41267         "Benin (Bénin)",
41268         "bj",
41269         "229"
41270       ],
41271       [
41272         "Bermuda",
41273         "bm",
41274         "1441"
41275       ],
41276       [
41277         "Bhutan (འབྲུག)",
41278         "bt",
41279         "975"
41280       ],
41281       [
41282         "Bolivia",
41283         "bo",
41284         "591"
41285       ],
41286       [
41287         "Bosnia and Herzegovina (Босна и Херцеговина)",
41288         "ba",
41289         "387"
41290       ],
41291       [
41292         "Botswana",
41293         "bw",
41294         "267"
41295       ],
41296       [
41297         "Brazil (Brasil)",
41298         "br",
41299         "55"
41300       ],
41301       [
41302         "British Indian Ocean Territory",
41303         "io",
41304         "246"
41305       ],
41306       [
41307         "British Virgin Islands",
41308         "vg",
41309         "1284"
41310       ],
41311       [
41312         "Brunei",
41313         "bn",
41314         "673"
41315       ],
41316       [
41317         "Bulgaria (България)",
41318         "bg",
41319         "359"
41320       ],
41321       [
41322         "Burkina Faso",
41323         "bf",
41324         "226"
41325       ],
41326       [
41327         "Burundi (Uburundi)",
41328         "bi",
41329         "257"
41330       ],
41331       [
41332         "Cambodia (កម្ពុជា)",
41333         "kh",
41334         "855"
41335       ],
41336       [
41337         "Cameroon (Cameroun)",
41338         "cm",
41339         "237"
41340       ],
41341       [
41342         "Canada",
41343         "ca",
41344         "1",
41345         1,
41346         ["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"]
41347       ],
41348       [
41349         "Cape Verde (Kabu Verdi)",
41350         "cv",
41351         "238"
41352       ],
41353       [
41354         "Caribbean Netherlands",
41355         "bq",
41356         "599",
41357         1
41358       ],
41359       [
41360         "Cayman Islands",
41361         "ky",
41362         "1345"
41363       ],
41364       [
41365         "Central African Republic (République centrafricaine)",
41366         "cf",
41367         "236"
41368       ],
41369       [
41370         "Chad (Tchad)",
41371         "td",
41372         "235"
41373       ],
41374       [
41375         "Chile",
41376         "cl",
41377         "56"
41378       ],
41379       [
41380         "China (中国)",
41381         "cn",
41382         "86"
41383       ],
41384       [
41385         "Christmas Island",
41386         "cx",
41387         "61",
41388         2
41389       ],
41390       [
41391         "Cocos (Keeling) Islands",
41392         "cc",
41393         "61",
41394         1
41395       ],
41396       [
41397         "Colombia",
41398         "co",
41399         "57"
41400       ],
41401       [
41402         "Comoros (‫جزر القمر‬‎)",
41403         "km",
41404         "269"
41405       ],
41406       [
41407         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41408         "cd",
41409         "243"
41410       ],
41411       [
41412         "Congo (Republic) (Congo-Brazzaville)",
41413         "cg",
41414         "242"
41415       ],
41416       [
41417         "Cook Islands",
41418         "ck",
41419         "682"
41420       ],
41421       [
41422         "Costa Rica",
41423         "cr",
41424         "506"
41425       ],
41426       [
41427         "Côte d’Ivoire",
41428         "ci",
41429         "225"
41430       ],
41431       [
41432         "Croatia (Hrvatska)",
41433         "hr",
41434         "385"
41435       ],
41436       [
41437         "Cuba",
41438         "cu",
41439         "53"
41440       ],
41441       [
41442         "Curaçao",
41443         "cw",
41444         "599",
41445         0
41446       ],
41447       [
41448         "Cyprus (Κύπρος)",
41449         "cy",
41450         "357"
41451       ],
41452       [
41453         "Czech Republic (Česká republika)",
41454         "cz",
41455         "420"
41456       ],
41457       [
41458         "Denmark (Danmark)",
41459         "dk",
41460         "45"
41461       ],
41462       [
41463         "Djibouti",
41464         "dj",
41465         "253"
41466       ],
41467       [
41468         "Dominica",
41469         "dm",
41470         "1767"
41471       ],
41472       [
41473         "Dominican Republic (República Dominicana)",
41474         "do",
41475         "1",
41476         2,
41477         ["809", "829", "849"]
41478       ],
41479       [
41480         "Ecuador",
41481         "ec",
41482         "593"
41483       ],
41484       [
41485         "Egypt (‫مصر‬‎)",
41486         "eg",
41487         "20"
41488       ],
41489       [
41490         "El Salvador",
41491         "sv",
41492         "503"
41493       ],
41494       [
41495         "Equatorial Guinea (Guinea Ecuatorial)",
41496         "gq",
41497         "240"
41498       ],
41499       [
41500         "Eritrea",
41501         "er",
41502         "291"
41503       ],
41504       [
41505         "Estonia (Eesti)",
41506         "ee",
41507         "372"
41508       ],
41509       [
41510         "Ethiopia",
41511         "et",
41512         "251"
41513       ],
41514       [
41515         "Falkland Islands (Islas Malvinas)",
41516         "fk",
41517         "500"
41518       ],
41519       [
41520         "Faroe Islands (Føroyar)",
41521         "fo",
41522         "298"
41523       ],
41524       [
41525         "Fiji",
41526         "fj",
41527         "679"
41528       ],
41529       [
41530         "Finland (Suomi)",
41531         "fi",
41532         "358",
41533         0
41534       ],
41535       [
41536         "France",
41537         "fr",
41538         "33"
41539       ],
41540       [
41541         "French Guiana (Guyane française)",
41542         "gf",
41543         "594"
41544       ],
41545       [
41546         "French Polynesia (Polynésie française)",
41547         "pf",
41548         "689"
41549       ],
41550       [
41551         "Gabon",
41552         "ga",
41553         "241"
41554       ],
41555       [
41556         "Gambia",
41557         "gm",
41558         "220"
41559       ],
41560       [
41561         "Georgia (საქართველო)",
41562         "ge",
41563         "995"
41564       ],
41565       [
41566         "Germany (Deutschland)",
41567         "de",
41568         "49"
41569       ],
41570       [
41571         "Ghana (Gaana)",
41572         "gh",
41573         "233"
41574       ],
41575       [
41576         "Gibraltar",
41577         "gi",
41578         "350"
41579       ],
41580       [
41581         "Greece (Ελλάδα)",
41582         "gr",
41583         "30"
41584       ],
41585       [
41586         "Greenland (Kalaallit Nunaat)",
41587         "gl",
41588         "299"
41589       ],
41590       [
41591         "Grenada",
41592         "gd",
41593         "1473"
41594       ],
41595       [
41596         "Guadeloupe",
41597         "gp",
41598         "590",
41599         0
41600       ],
41601       [
41602         "Guam",
41603         "gu",
41604         "1671"
41605       ],
41606       [
41607         "Guatemala",
41608         "gt",
41609         "502"
41610       ],
41611       [
41612         "Guernsey",
41613         "gg",
41614         "44",
41615         1
41616       ],
41617       [
41618         "Guinea (Guinée)",
41619         "gn",
41620         "224"
41621       ],
41622       [
41623         "Guinea-Bissau (Guiné Bissau)",
41624         "gw",
41625         "245"
41626       ],
41627       [
41628         "Guyana",
41629         "gy",
41630         "592"
41631       ],
41632       [
41633         "Haiti",
41634         "ht",
41635         "509"
41636       ],
41637       [
41638         "Honduras",
41639         "hn",
41640         "504"
41641       ],
41642       [
41643         "Hong Kong (香港)",
41644         "hk",
41645         "852"
41646       ],
41647       [
41648         "Hungary (Magyarország)",
41649         "hu",
41650         "36"
41651       ],
41652       [
41653         "Iceland (Ísland)",
41654         "is",
41655         "354"
41656       ],
41657       [
41658         "India (भारत)",
41659         "in",
41660         "91"
41661       ],
41662       [
41663         "Indonesia",
41664         "id",
41665         "62"
41666       ],
41667       [
41668         "Iran (‫ایران‬‎)",
41669         "ir",
41670         "98"
41671       ],
41672       [
41673         "Iraq (‫العراق‬‎)",
41674         "iq",
41675         "964"
41676       ],
41677       [
41678         "Ireland",
41679         "ie",
41680         "353"
41681       ],
41682       [
41683         "Isle of Man",
41684         "im",
41685         "44",
41686         2
41687       ],
41688       [
41689         "Israel (‫ישראל‬‎)",
41690         "il",
41691         "972"
41692       ],
41693       [
41694         "Italy (Italia)",
41695         "it",
41696         "39",
41697         0
41698       ],
41699       [
41700         "Jamaica",
41701         "jm",
41702         "1876"
41703       ],
41704       [
41705         "Japan (日本)",
41706         "jp",
41707         "81"
41708       ],
41709       [
41710         "Jersey",
41711         "je",
41712         "44",
41713         3
41714       ],
41715       [
41716         "Jordan (‫الأردن‬‎)",
41717         "jo",
41718         "962"
41719       ],
41720       [
41721         "Kazakhstan (Казахстан)",
41722         "kz",
41723         "7",
41724         1
41725       ],
41726       [
41727         "Kenya",
41728         "ke",
41729         "254"
41730       ],
41731       [
41732         "Kiribati",
41733         "ki",
41734         "686"
41735       ],
41736       [
41737         "Kosovo",
41738         "xk",
41739         "383"
41740       ],
41741       [
41742         "Kuwait (‫الكويت‬‎)",
41743         "kw",
41744         "965"
41745       ],
41746       [
41747         "Kyrgyzstan (Кыргызстан)",
41748         "kg",
41749         "996"
41750       ],
41751       [
41752         "Laos (ລາວ)",
41753         "la",
41754         "856"
41755       ],
41756       [
41757         "Latvia (Latvija)",
41758         "lv",
41759         "371"
41760       ],
41761       [
41762         "Lebanon (‫لبنان‬‎)",
41763         "lb",
41764         "961"
41765       ],
41766       [
41767         "Lesotho",
41768         "ls",
41769         "266"
41770       ],
41771       [
41772         "Liberia",
41773         "lr",
41774         "231"
41775       ],
41776       [
41777         "Libya (‫ليبيا‬‎)",
41778         "ly",
41779         "218"
41780       ],
41781       [
41782         "Liechtenstein",
41783         "li",
41784         "423"
41785       ],
41786       [
41787         "Lithuania (Lietuva)",
41788         "lt",
41789         "370"
41790       ],
41791       [
41792         "Luxembourg",
41793         "lu",
41794         "352"
41795       ],
41796       [
41797         "Macau (澳門)",
41798         "mo",
41799         "853"
41800       ],
41801       [
41802         "Macedonia (FYROM) (Македонија)",
41803         "mk",
41804         "389"
41805       ],
41806       [
41807         "Madagascar (Madagasikara)",
41808         "mg",
41809         "261"
41810       ],
41811       [
41812         "Malawi",
41813         "mw",
41814         "265"
41815       ],
41816       [
41817         "Malaysia",
41818         "my",
41819         "60"
41820       ],
41821       [
41822         "Maldives",
41823         "mv",
41824         "960"
41825       ],
41826       [
41827         "Mali",
41828         "ml",
41829         "223"
41830       ],
41831       [
41832         "Malta",
41833         "mt",
41834         "356"
41835       ],
41836       [
41837         "Marshall Islands",
41838         "mh",
41839         "692"
41840       ],
41841       [
41842         "Martinique",
41843         "mq",
41844         "596"
41845       ],
41846       [
41847         "Mauritania (‫موريتانيا‬‎)",
41848         "mr",
41849         "222"
41850       ],
41851       [
41852         "Mauritius (Moris)",
41853         "mu",
41854         "230"
41855       ],
41856       [
41857         "Mayotte",
41858         "yt",
41859         "262",
41860         1
41861       ],
41862       [
41863         "Mexico (México)",
41864         "mx",
41865         "52"
41866       ],
41867       [
41868         "Micronesia",
41869         "fm",
41870         "691"
41871       ],
41872       [
41873         "Moldova (Republica Moldova)",
41874         "md",
41875         "373"
41876       ],
41877       [
41878         "Monaco",
41879         "mc",
41880         "377"
41881       ],
41882       [
41883         "Mongolia (Монгол)",
41884         "mn",
41885         "976"
41886       ],
41887       [
41888         "Montenegro (Crna Gora)",
41889         "me",
41890         "382"
41891       ],
41892       [
41893         "Montserrat",
41894         "ms",
41895         "1664"
41896       ],
41897       [
41898         "Morocco (‫المغرب‬‎)",
41899         "ma",
41900         "212",
41901         0
41902       ],
41903       [
41904         "Mozambique (Moçambique)",
41905         "mz",
41906         "258"
41907       ],
41908       [
41909         "Myanmar (Burma) (မြန်မာ)",
41910         "mm",
41911         "95"
41912       ],
41913       [
41914         "Namibia (Namibië)",
41915         "na",
41916         "264"
41917       ],
41918       [
41919         "Nauru",
41920         "nr",
41921         "674"
41922       ],
41923       [
41924         "Nepal (नेपाल)",
41925         "np",
41926         "977"
41927       ],
41928       [
41929         "Netherlands (Nederland)",
41930         "nl",
41931         "31"
41932       ],
41933       [
41934         "New Caledonia (Nouvelle-Calédonie)",
41935         "nc",
41936         "687"
41937       ],
41938       [
41939         "New Zealand",
41940         "nz",
41941         "64"
41942       ],
41943       [
41944         "Nicaragua",
41945         "ni",
41946         "505"
41947       ],
41948       [
41949         "Niger (Nijar)",
41950         "ne",
41951         "227"
41952       ],
41953       [
41954         "Nigeria",
41955         "ng",
41956         "234"
41957       ],
41958       [
41959         "Niue",
41960         "nu",
41961         "683"
41962       ],
41963       [
41964         "Norfolk Island",
41965         "nf",
41966         "672"
41967       ],
41968       [
41969         "North Korea (조선 민주주의 인민 공화국)",
41970         "kp",
41971         "850"
41972       ],
41973       [
41974         "Northern Mariana Islands",
41975         "mp",
41976         "1670"
41977       ],
41978       [
41979         "Norway (Norge)",
41980         "no",
41981         "47",
41982         0
41983       ],
41984       [
41985         "Oman (‫عُمان‬‎)",
41986         "om",
41987         "968"
41988       ],
41989       [
41990         "Pakistan (‫پاکستان‬‎)",
41991         "pk",
41992         "92"
41993       ],
41994       [
41995         "Palau",
41996         "pw",
41997         "680"
41998       ],
41999       [
42000         "Palestine (‫فلسطين‬‎)",
42001         "ps",
42002         "970"
42003       ],
42004       [
42005         "Panama (Panamá)",
42006         "pa",
42007         "507"
42008       ],
42009       [
42010         "Papua New Guinea",
42011         "pg",
42012         "675"
42013       ],
42014       [
42015         "Paraguay",
42016         "py",
42017         "595"
42018       ],
42019       [
42020         "Peru (Perú)",
42021         "pe",
42022         "51"
42023       ],
42024       [
42025         "Philippines",
42026         "ph",
42027         "63"
42028       ],
42029       [
42030         "Poland (Polska)",
42031         "pl",
42032         "48"
42033       ],
42034       [
42035         "Portugal",
42036         "pt",
42037         "351"
42038       ],
42039       [
42040         "Puerto Rico",
42041         "pr",
42042         "1",
42043         3,
42044         ["787", "939"]
42045       ],
42046       [
42047         "Qatar (‫قطر‬‎)",
42048         "qa",
42049         "974"
42050       ],
42051       [
42052         "Réunion (La Réunion)",
42053         "re",
42054         "262",
42055         0
42056       ],
42057       [
42058         "Romania (România)",
42059         "ro",
42060         "40"
42061       ],
42062       [
42063         "Russia (Россия)",
42064         "ru",
42065         "7",
42066         0
42067       ],
42068       [
42069         "Rwanda",
42070         "rw",
42071         "250"
42072       ],
42073       [
42074         "Saint Barthélemy",
42075         "bl",
42076         "590",
42077         1
42078       ],
42079       [
42080         "Saint Helena",
42081         "sh",
42082         "290"
42083       ],
42084       [
42085         "Saint Kitts and Nevis",
42086         "kn",
42087         "1869"
42088       ],
42089       [
42090         "Saint Lucia",
42091         "lc",
42092         "1758"
42093       ],
42094       [
42095         "Saint Martin (Saint-Martin (partie française))",
42096         "mf",
42097         "590",
42098         2
42099       ],
42100       [
42101         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42102         "pm",
42103         "508"
42104       ],
42105       [
42106         "Saint Vincent and the Grenadines",
42107         "vc",
42108         "1784"
42109       ],
42110       [
42111         "Samoa",
42112         "ws",
42113         "685"
42114       ],
42115       [
42116         "San Marino",
42117         "sm",
42118         "378"
42119       ],
42120       [
42121         "São Tomé and Príncipe (São Tomé e Príncipe)",
42122         "st",
42123         "239"
42124       ],
42125       [
42126         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42127         "sa",
42128         "966"
42129       ],
42130       [
42131         "Senegal (Sénégal)",
42132         "sn",
42133         "221"
42134       ],
42135       [
42136         "Serbia (Србија)",
42137         "rs",
42138         "381"
42139       ],
42140       [
42141         "Seychelles",
42142         "sc",
42143         "248"
42144       ],
42145       [
42146         "Sierra Leone",
42147         "sl",
42148         "232"
42149       ],
42150       [
42151         "Singapore",
42152         "sg",
42153         "65"
42154       ],
42155       [
42156         "Sint Maarten",
42157         "sx",
42158         "1721"
42159       ],
42160       [
42161         "Slovakia (Slovensko)",
42162         "sk",
42163         "421"
42164       ],
42165       [
42166         "Slovenia (Slovenija)",
42167         "si",
42168         "386"
42169       ],
42170       [
42171         "Solomon Islands",
42172         "sb",
42173         "677"
42174       ],
42175       [
42176         "Somalia (Soomaaliya)",
42177         "so",
42178         "252"
42179       ],
42180       [
42181         "South Africa",
42182         "za",
42183         "27"
42184       ],
42185       [
42186         "South Korea (대한민국)",
42187         "kr",
42188         "82"
42189       ],
42190       [
42191         "South Sudan (‫جنوب السودان‬‎)",
42192         "ss",
42193         "211"
42194       ],
42195       [
42196         "Spain (España)",
42197         "es",
42198         "34"
42199       ],
42200       [
42201         "Sri Lanka (ශ්‍රී ලංකාව)",
42202         "lk",
42203         "94"
42204       ],
42205       [
42206         "Sudan (‫السودان‬‎)",
42207         "sd",
42208         "249"
42209       ],
42210       [
42211         "Suriname",
42212         "sr",
42213         "597"
42214       ],
42215       [
42216         "Svalbard and Jan Mayen",
42217         "sj",
42218         "47",
42219         1
42220       ],
42221       [
42222         "Swaziland",
42223         "sz",
42224         "268"
42225       ],
42226       [
42227         "Sweden (Sverige)",
42228         "se",
42229         "46"
42230       ],
42231       [
42232         "Switzerland (Schweiz)",
42233         "ch",
42234         "41"
42235       ],
42236       [
42237         "Syria (‫سوريا‬‎)",
42238         "sy",
42239         "963"
42240       ],
42241       [
42242         "Taiwan (台灣)",
42243         "tw",
42244         "886"
42245       ],
42246       [
42247         "Tajikistan",
42248         "tj",
42249         "992"
42250       ],
42251       [
42252         "Tanzania",
42253         "tz",
42254         "255"
42255       ],
42256       [
42257         "Thailand (ไทย)",
42258         "th",
42259         "66"
42260       ],
42261       [
42262         "Timor-Leste",
42263         "tl",
42264         "670"
42265       ],
42266       [
42267         "Togo",
42268         "tg",
42269         "228"
42270       ],
42271       [
42272         "Tokelau",
42273         "tk",
42274         "690"
42275       ],
42276       [
42277         "Tonga",
42278         "to",
42279         "676"
42280       ],
42281       [
42282         "Trinidad and Tobago",
42283         "tt",
42284         "1868"
42285       ],
42286       [
42287         "Tunisia (‫تونس‬‎)",
42288         "tn",
42289         "216"
42290       ],
42291       [
42292         "Turkey (Türkiye)",
42293         "tr",
42294         "90"
42295       ],
42296       [
42297         "Turkmenistan",
42298         "tm",
42299         "993"
42300       ],
42301       [
42302         "Turks and Caicos Islands",
42303         "tc",
42304         "1649"
42305       ],
42306       [
42307         "Tuvalu",
42308         "tv",
42309         "688"
42310       ],
42311       [
42312         "U.S. Virgin Islands",
42313         "vi",
42314         "1340"
42315       ],
42316       [
42317         "Uganda",
42318         "ug",
42319         "256"
42320       ],
42321       [
42322         "Ukraine (Україна)",
42323         "ua",
42324         "380"
42325       ],
42326       [
42327         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42328         "ae",
42329         "971"
42330       ],
42331       [
42332         "United Kingdom",
42333         "gb",
42334         "44",
42335         0
42336       ],
42337       [
42338         "United States",
42339         "us",
42340         "1",
42341         0
42342       ],
42343       [
42344         "Uruguay",
42345         "uy",
42346         "598"
42347       ],
42348       [
42349         "Uzbekistan (Oʻzbekiston)",
42350         "uz",
42351         "998"
42352       ],
42353       [
42354         "Vanuatu",
42355         "vu",
42356         "678"
42357       ],
42358       [
42359         "Vatican City (Città del Vaticano)",
42360         "va",
42361         "39",
42362         1
42363       ],
42364       [
42365         "Venezuela",
42366         "ve",
42367         "58"
42368       ],
42369       [
42370         "Vietnam (Việt Nam)",
42371         "vn",
42372         "84"
42373       ],
42374       [
42375         "Wallis and Futuna (Wallis-et-Futuna)",
42376         "wf",
42377         "681"
42378       ],
42379       [
42380         "Western Sahara (‫الصحراء الغربية‬‎)",
42381         "eh",
42382         "212",
42383         1
42384       ],
42385       [
42386         "Yemen (‫اليمن‬‎)",
42387         "ye",
42388         "967"
42389       ],
42390       [
42391         "Zambia",
42392         "zm",
42393         "260"
42394       ],
42395       [
42396         "Zimbabwe",
42397         "zw",
42398         "263"
42399       ],
42400       [
42401         "Åland Islands",
42402         "ax",
42403         "358",
42404         1
42405       ]
42406   ];
42407   
42408   return d;
42409 }/**
42410 *    This script refer to:
42411 *    Title: International Telephone Input
42412 *    Author: Jack O'Connor
42413 *    Code version:  v12.1.12
42414 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42415 **/
42416
42417 /**
42418  * @class Roo.bootstrap.PhoneInput
42419  * @extends Roo.bootstrap.TriggerField
42420  * An input with International dial-code selection
42421  
42422  * @cfg {String} defaultDialCode default '+852'
42423  * @cfg {Array} preferedCountries default []
42424   
42425  * @constructor
42426  * Create a new PhoneInput.
42427  * @param {Object} config Configuration options
42428  */
42429
42430 Roo.bootstrap.PhoneInput = function(config) {
42431     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42432 };
42433
42434 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42435         
42436         listWidth: undefined,
42437         
42438         selectedClass: 'active',
42439         
42440         invalidClass : "has-warning",
42441         
42442         validClass: 'has-success',
42443         
42444         allowed: '0123456789',
42445         
42446         max_length: 15,
42447         
42448         /**
42449          * @cfg {String} defaultDialCode The default dial code when initializing the input
42450          */
42451         defaultDialCode: '+852',
42452         
42453         /**
42454          * @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
42455          */
42456         preferedCountries: false,
42457         
42458         getAutoCreate : function()
42459         {
42460             var data = Roo.bootstrap.PhoneInputData();
42461             var align = this.labelAlign || this.parentLabelAlign();
42462             var id = Roo.id();
42463             
42464             this.allCountries = [];
42465             this.dialCodeMapping = [];
42466             
42467             for (var i = 0; i < data.length; i++) {
42468               var c = data[i];
42469               this.allCountries[i] = {
42470                 name: c[0],
42471                 iso2: c[1],
42472                 dialCode: c[2],
42473                 priority: c[3] || 0,
42474                 areaCodes: c[4] || null
42475               };
42476               this.dialCodeMapping[c[2]] = {
42477                   name: c[0],
42478                   iso2: c[1],
42479                   priority: c[3] || 0,
42480                   areaCodes: c[4] || null
42481               };
42482             }
42483             
42484             var cfg = {
42485                 cls: 'form-group',
42486                 cn: []
42487             };
42488             
42489             var input =  {
42490                 tag: 'input',
42491                 id : id,
42492                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42493                 maxlength: this.max_length,
42494                 cls : 'form-control tel-input',
42495                 autocomplete: 'new-password'
42496             };
42497             
42498             var hiddenInput = {
42499                 tag: 'input',
42500                 type: 'hidden',
42501                 cls: 'hidden-tel-input'
42502             };
42503             
42504             if (this.name) {
42505                 hiddenInput.name = this.name;
42506             }
42507             
42508             if (this.disabled) {
42509                 input.disabled = true;
42510             }
42511             
42512             var flag_container = {
42513                 tag: 'div',
42514                 cls: 'flag-box',
42515                 cn: [
42516                     {
42517                         tag: 'div',
42518                         cls: 'flag'
42519                     },
42520                     {
42521                         tag: 'div',
42522                         cls: 'caret'
42523                     }
42524                 ]
42525             };
42526             
42527             var box = {
42528                 tag: 'div',
42529                 cls: this.hasFeedback ? 'has-feedback' : '',
42530                 cn: [
42531                     hiddenInput,
42532                     input,
42533                     {
42534                         tag: 'input',
42535                         cls: 'dial-code-holder',
42536                         disabled: true
42537                     }
42538                 ]
42539             };
42540             
42541             var container = {
42542                 cls: 'roo-select2-container input-group',
42543                 cn: [
42544                     flag_container,
42545                     box
42546                 ]
42547             };
42548             
42549             if (this.fieldLabel.length) {
42550                 var indicator = {
42551                     tag: 'i',
42552                     tooltip: 'This field is required'
42553                 };
42554                 
42555                 var label = {
42556                     tag: 'label',
42557                     'for':  id,
42558                     cls: 'control-label',
42559                     cn: []
42560                 };
42561                 
42562                 var label_text = {
42563                     tag: 'span',
42564                     html: this.fieldLabel
42565                 };
42566                 
42567                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42568                 label.cn = [
42569                     indicator,
42570                     label_text
42571                 ];
42572                 
42573                 if(this.indicatorpos == 'right') {
42574                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42575                     label.cn = [
42576                         label_text,
42577                         indicator
42578                     ];
42579                 }
42580                 
42581                 if(align == 'left') {
42582                     container = {
42583                         tag: 'div',
42584                         cn: [
42585                             container
42586                         ]
42587                     };
42588                     
42589                     if(this.labelWidth > 12){
42590                         label.style = "width: " + this.labelWidth + 'px';
42591                     }
42592                     if(this.labelWidth < 13 && this.labelmd == 0){
42593                         this.labelmd = this.labelWidth;
42594                     }
42595                     if(this.labellg > 0){
42596                         label.cls += ' col-lg-' + this.labellg;
42597                         input.cls += ' col-lg-' + (12 - this.labellg);
42598                     }
42599                     if(this.labelmd > 0){
42600                         label.cls += ' col-md-' + this.labelmd;
42601                         container.cls += ' col-md-' + (12 - this.labelmd);
42602                     }
42603                     if(this.labelsm > 0){
42604                         label.cls += ' col-sm-' + this.labelsm;
42605                         container.cls += ' col-sm-' + (12 - this.labelsm);
42606                     }
42607                     if(this.labelxs > 0){
42608                         label.cls += ' col-xs-' + this.labelxs;
42609                         container.cls += ' col-xs-' + (12 - this.labelxs);
42610                     }
42611                 }
42612             }
42613             
42614             cfg.cn = [
42615                 label,
42616                 container
42617             ];
42618             
42619             var settings = this;
42620             
42621             ['xs','sm','md','lg'].map(function(size){
42622                 if (settings[size]) {
42623                     cfg.cls += ' col-' + size + '-' + settings[size];
42624                 }
42625             });
42626             
42627             this.store = new Roo.data.Store({
42628                 proxy : new Roo.data.MemoryProxy({}),
42629                 reader : new Roo.data.JsonReader({
42630                     fields : [
42631                         {
42632                             'name' : 'name',
42633                             'type' : 'string'
42634                         },
42635                         {
42636                             'name' : 'iso2',
42637                             'type' : 'string'
42638                         },
42639                         {
42640                             'name' : 'dialCode',
42641                             'type' : 'string'
42642                         },
42643                         {
42644                             'name' : 'priority',
42645                             'type' : 'string'
42646                         },
42647                         {
42648                             'name' : 'areaCodes',
42649                             'type' : 'string'
42650                         }
42651                     ]
42652                 })
42653             });
42654             
42655             if(!this.preferedCountries) {
42656                 this.preferedCountries = [
42657                     'hk',
42658                     'gb',
42659                     'us'
42660                 ];
42661             }
42662             
42663             var p = this.preferedCountries.reverse();
42664             
42665             if(p) {
42666                 for (var i = 0; i < p.length; i++) {
42667                     for (var j = 0; j < this.allCountries.length; j++) {
42668                         if(this.allCountries[j].iso2 == p[i]) {
42669                             var t = this.allCountries[j];
42670                             this.allCountries.splice(j,1);
42671                             this.allCountries.unshift(t);
42672                         }
42673                     } 
42674                 }
42675             }
42676             
42677             this.store.proxy.data = {
42678                 success: true,
42679                 data: this.allCountries
42680             };
42681             
42682             return cfg;
42683         },
42684         
42685         initEvents : function()
42686         {
42687             this.createList();
42688             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42689             
42690             this.indicator = this.indicatorEl();
42691             this.flag = this.flagEl();
42692             this.dialCodeHolder = this.dialCodeHolderEl();
42693             
42694             this.trigger = this.el.select('div.flag-box',true).first();
42695             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42696             
42697             var _this = this;
42698             
42699             (function(){
42700                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42701                 _this.list.setWidth(lw);
42702             }).defer(100);
42703             
42704             this.list.on('mouseover', this.onViewOver, this);
42705             this.list.on('mousemove', this.onViewMove, this);
42706             this.inputEl().on("keyup", this.onKeyUp, this);
42707             this.inputEl().on("keypress", this.onKeyPress, this);
42708             
42709             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42710
42711             this.view = new Roo.View(this.list, this.tpl, {
42712                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42713             });
42714             
42715             this.view.on('click', this.onViewClick, this);
42716             this.setValue(this.defaultDialCode);
42717         },
42718         
42719         onTriggerClick : function(e)
42720         {
42721             Roo.log('trigger click');
42722             if(this.disabled){
42723                 return;
42724             }
42725             
42726             if(this.isExpanded()){
42727                 this.collapse();
42728                 this.hasFocus = false;
42729             }else {
42730                 this.store.load({});
42731                 this.hasFocus = true;
42732                 this.expand();
42733             }
42734         },
42735         
42736         isExpanded : function()
42737         {
42738             return this.list.isVisible();
42739         },
42740         
42741         collapse : function()
42742         {
42743             if(!this.isExpanded()){
42744                 return;
42745             }
42746             this.list.hide();
42747             Roo.get(document).un('mousedown', this.collapseIf, this);
42748             Roo.get(document).un('mousewheel', this.collapseIf, this);
42749             this.fireEvent('collapse', this);
42750             this.validate();
42751         },
42752         
42753         expand : function()
42754         {
42755             Roo.log('expand');
42756
42757             if(this.isExpanded() || !this.hasFocus){
42758                 return;
42759             }
42760             
42761             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42762             this.list.setWidth(lw);
42763             
42764             this.list.show();
42765             this.restrictHeight();
42766             
42767             Roo.get(document).on('mousedown', this.collapseIf, this);
42768             Roo.get(document).on('mousewheel', this.collapseIf, this);
42769             
42770             this.fireEvent('expand', this);
42771         },
42772         
42773         restrictHeight : function()
42774         {
42775             this.list.alignTo(this.inputEl(), this.listAlign);
42776             this.list.alignTo(this.inputEl(), this.listAlign);
42777         },
42778         
42779         onViewOver : function(e, t)
42780         {
42781             if(this.inKeyMode){
42782                 return;
42783             }
42784             var item = this.view.findItemFromChild(t);
42785             
42786             if(item){
42787                 var index = this.view.indexOf(item);
42788                 this.select(index, false);
42789             }
42790         },
42791
42792         // private
42793         onViewClick : function(view, doFocus, el, e)
42794         {
42795             var index = this.view.getSelectedIndexes()[0];
42796             
42797             var r = this.store.getAt(index);
42798             
42799             if(r){
42800                 this.onSelect(r, index);
42801             }
42802             if(doFocus !== false && !this.blockFocus){
42803                 this.inputEl().focus();
42804             }
42805         },
42806         
42807         onViewMove : function(e, t)
42808         {
42809             this.inKeyMode = false;
42810         },
42811         
42812         select : function(index, scrollIntoView)
42813         {
42814             this.selectedIndex = index;
42815             this.view.select(index);
42816             if(scrollIntoView !== false){
42817                 var el = this.view.getNode(index);
42818                 if(el){
42819                     this.list.scrollChildIntoView(el, false);
42820                 }
42821             }
42822         },
42823         
42824         createList : function()
42825         {
42826             this.list = Roo.get(document.body).createChild({
42827                 tag: 'ul',
42828                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42829                 style: 'display:none'
42830             });
42831             
42832             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42833         },
42834         
42835         collapseIf : function(e)
42836         {
42837             var in_combo  = e.within(this.el);
42838             var in_list =  e.within(this.list);
42839             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42840             
42841             if (in_combo || in_list || is_list) {
42842                 return;
42843             }
42844             this.collapse();
42845         },
42846         
42847         onSelect : function(record, index)
42848         {
42849             if(this.fireEvent('beforeselect', this, record, index) !== false){
42850                 
42851                 this.setFlagClass(record.data.iso2);
42852                 this.setDialCode(record.data.dialCode);
42853                 this.hasFocus = false;
42854                 this.collapse();
42855                 this.fireEvent('select', this, record, index);
42856             }
42857         },
42858         
42859         flagEl : function()
42860         {
42861             var flag = this.el.select('div.flag',true).first();
42862             if(!flag){
42863                 return false;
42864             }
42865             return flag;
42866         },
42867         
42868         dialCodeHolderEl : function()
42869         {
42870             var d = this.el.select('input.dial-code-holder',true).first();
42871             if(!d){
42872                 return false;
42873             }
42874             return d;
42875         },
42876         
42877         setDialCode : function(v)
42878         {
42879             this.dialCodeHolder.dom.value = '+'+v;
42880         },
42881         
42882         setFlagClass : function(n)
42883         {
42884             this.flag.dom.className = 'flag '+n;
42885         },
42886         
42887         getValue : function()
42888         {
42889             var v = this.inputEl().getValue();
42890             if(this.dialCodeHolder) {
42891                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42892             }
42893             return v;
42894         },
42895         
42896         setValue : function(v)
42897         {
42898             var d = this.getDialCode(v);
42899             
42900             //invalid dial code
42901             if(v.length == 0 || !d || d.length == 0) {
42902                 if(this.rendered){
42903                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42904                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42905                 }
42906                 return;
42907             }
42908             
42909             //valid dial code
42910             this.setFlagClass(this.dialCodeMapping[d].iso2);
42911             this.setDialCode(d);
42912             this.inputEl().dom.value = v.replace('+'+d,'');
42913             this.hiddenEl().dom.value = this.getValue();
42914             
42915             this.validate();
42916         },
42917         
42918         getDialCode : function(v)
42919         {
42920             v = v ||  '';
42921             
42922             if (v.length == 0) {
42923                 return this.dialCodeHolder.dom.value;
42924             }
42925             
42926             var dialCode = "";
42927             if (v.charAt(0) != "+") {
42928                 return false;
42929             }
42930             var numericChars = "";
42931             for (var i = 1; i < v.length; i++) {
42932               var c = v.charAt(i);
42933               if (!isNaN(c)) {
42934                 numericChars += c;
42935                 if (this.dialCodeMapping[numericChars]) {
42936                   dialCode = v.substr(1, i);
42937                 }
42938                 if (numericChars.length == 4) {
42939                   break;
42940                 }
42941               }
42942             }
42943             return dialCode;
42944         },
42945         
42946         reset : function()
42947         {
42948             this.setValue(this.defaultDialCode);
42949             this.validate();
42950         },
42951         
42952         hiddenEl : function()
42953         {
42954             return this.el.select('input.hidden-tel-input',true).first();
42955         },
42956         
42957         // after setting val
42958         onKeyUp : function(e){
42959             this.setValue(this.getValue());
42960         },
42961         
42962         onKeyPress : function(e){
42963             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42964                 e.stopEvent();
42965             }
42966         }
42967         
42968 });
42969 /**
42970  * @class Roo.bootstrap.MoneyField
42971  * @extends Roo.bootstrap.ComboBox
42972  * Bootstrap MoneyField class
42973  * 
42974  * @constructor
42975  * Create a new MoneyField.
42976  * @param {Object} config Configuration options
42977  */
42978
42979 Roo.bootstrap.MoneyField = function(config) {
42980     
42981     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42982     
42983 };
42984
42985 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42986     
42987     /**
42988      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42989      */
42990     allowDecimals : true,
42991     /**
42992      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42993      */
42994     decimalSeparator : ".",
42995     /**
42996      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42997      */
42998     decimalPrecision : 0,
42999     /**
43000      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43001      */
43002     allowNegative : true,
43003     /**
43004      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43005      */
43006     allowZero: true,
43007     /**
43008      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43009      */
43010     minValue : Number.NEGATIVE_INFINITY,
43011     /**
43012      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43013      */
43014     maxValue : Number.MAX_VALUE,
43015     /**
43016      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43017      */
43018     minText : "The minimum value for this field is {0}",
43019     /**
43020      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43021      */
43022     maxText : "The maximum value for this field is {0}",
43023     /**
43024      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43025      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43026      */
43027     nanText : "{0} is not a valid number",
43028     /**
43029      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43030      */
43031     castInt : true,
43032     /**
43033      * @cfg {String} defaults currency of the MoneyField
43034      * value should be in lkey
43035      */
43036     defaultCurrency : false,
43037     /**
43038      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43039      */
43040     thousandsDelimiter : false,
43041     /**
43042      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43043      */
43044     max_length: false,
43045     
43046     inputlg : 9,
43047     inputmd : 9,
43048     inputsm : 9,
43049     inputxs : 6,
43050     
43051     store : false,
43052     
43053     getAutoCreate : function()
43054     {
43055         var align = this.labelAlign || this.parentLabelAlign();
43056         
43057         var id = Roo.id();
43058
43059         var cfg = {
43060             cls: 'form-group',
43061             cn: []
43062         };
43063
43064         var input =  {
43065             tag: 'input',
43066             id : id,
43067             cls : 'form-control roo-money-amount-input',
43068             autocomplete: 'new-password'
43069         };
43070         
43071         var hiddenInput = {
43072             tag: 'input',
43073             type: 'hidden',
43074             id: Roo.id(),
43075             cls: 'hidden-number-input'
43076         };
43077         
43078         if(this.max_length) {
43079             input.maxlength = this.max_length; 
43080         }
43081         
43082         if (this.name) {
43083             hiddenInput.name = this.name;
43084         }
43085
43086         if (this.disabled) {
43087             input.disabled = true;
43088         }
43089
43090         var clg = 12 - this.inputlg;
43091         var cmd = 12 - this.inputmd;
43092         var csm = 12 - this.inputsm;
43093         var cxs = 12 - this.inputxs;
43094         
43095         var container = {
43096             tag : 'div',
43097             cls : 'row roo-money-field',
43098             cn : [
43099                 {
43100                     tag : 'div',
43101                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43102                     cn : [
43103                         {
43104                             tag : 'div',
43105                             cls: 'roo-select2-container input-group',
43106                             cn: [
43107                                 {
43108                                     tag : 'input',
43109                                     cls : 'form-control roo-money-currency-input',
43110                                     autocomplete: 'new-password',
43111                                     readOnly : 1,
43112                                     name : this.currencyName
43113                                 },
43114                                 {
43115                                     tag :'span',
43116                                     cls : 'input-group-addon',
43117                                     cn : [
43118                                         {
43119                                             tag: 'span',
43120                                             cls: 'caret'
43121                                         }
43122                                     ]
43123                                 }
43124                             ]
43125                         }
43126                     ]
43127                 },
43128                 {
43129                     tag : 'div',
43130                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43131                     cn : [
43132                         {
43133                             tag: 'div',
43134                             cls: this.hasFeedback ? 'has-feedback' : '',
43135                             cn: [
43136                                 input
43137                             ]
43138                         }
43139                     ]
43140                 }
43141             ]
43142             
43143         };
43144         
43145         if (this.fieldLabel.length) {
43146             var indicator = {
43147                 tag: 'i',
43148                 tooltip: 'This field is required'
43149             };
43150
43151             var label = {
43152                 tag: 'label',
43153                 'for':  id,
43154                 cls: 'control-label',
43155                 cn: []
43156             };
43157
43158             var label_text = {
43159                 tag: 'span',
43160                 html: this.fieldLabel
43161             };
43162
43163             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43164             label.cn = [
43165                 indicator,
43166                 label_text
43167             ];
43168
43169             if(this.indicatorpos == 'right') {
43170                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43171                 label.cn = [
43172                     label_text,
43173                     indicator
43174                 ];
43175             }
43176
43177             if(align == 'left') {
43178                 container = {
43179                     tag: 'div',
43180                     cn: [
43181                         container
43182                     ]
43183                 };
43184
43185                 if(this.labelWidth > 12){
43186                     label.style = "width: " + this.labelWidth + 'px';
43187                 }
43188                 if(this.labelWidth < 13 && this.labelmd == 0){
43189                     this.labelmd = this.labelWidth;
43190                 }
43191                 if(this.labellg > 0){
43192                     label.cls += ' col-lg-' + this.labellg;
43193                     input.cls += ' col-lg-' + (12 - this.labellg);
43194                 }
43195                 if(this.labelmd > 0){
43196                     label.cls += ' col-md-' + this.labelmd;
43197                     container.cls += ' col-md-' + (12 - this.labelmd);
43198                 }
43199                 if(this.labelsm > 0){
43200                     label.cls += ' col-sm-' + this.labelsm;
43201                     container.cls += ' col-sm-' + (12 - this.labelsm);
43202                 }
43203                 if(this.labelxs > 0){
43204                     label.cls += ' col-xs-' + this.labelxs;
43205                     container.cls += ' col-xs-' + (12 - this.labelxs);
43206                 }
43207             }
43208         }
43209
43210         cfg.cn = [
43211             label,
43212             container,
43213             hiddenInput
43214         ];
43215         
43216         var settings = this;
43217
43218         ['xs','sm','md','lg'].map(function(size){
43219             if (settings[size]) {
43220                 cfg.cls += ' col-' + size + '-' + settings[size];
43221             }
43222         });
43223         
43224         return cfg;
43225     },
43226     
43227     initEvents : function()
43228     {
43229         this.indicator = this.indicatorEl();
43230         
43231         this.initCurrencyEvent();
43232         
43233         this.initNumberEvent();
43234     },
43235     
43236     initCurrencyEvent : function()
43237     {
43238         if (!this.store) {
43239             throw "can not find store for combo";
43240         }
43241         
43242         this.store = Roo.factory(this.store, Roo.data);
43243         this.store.parent = this;
43244         
43245         this.createList();
43246         
43247         this.triggerEl = this.el.select('.input-group-addon', true).first();
43248         
43249         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43250         
43251         var _this = this;
43252         
43253         (function(){
43254             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43255             _this.list.setWidth(lw);
43256         }).defer(100);
43257         
43258         this.list.on('mouseover', this.onViewOver, this);
43259         this.list.on('mousemove', this.onViewMove, this);
43260         this.list.on('scroll', this.onViewScroll, this);
43261         
43262         if(!this.tpl){
43263             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43264         }
43265         
43266         this.view = new Roo.View(this.list, this.tpl, {
43267             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43268         });
43269         
43270         this.view.on('click', this.onViewClick, this);
43271         
43272         this.store.on('beforeload', this.onBeforeLoad, this);
43273         this.store.on('load', this.onLoad, this);
43274         this.store.on('loadexception', this.onLoadException, this);
43275         
43276         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43277             "up" : function(e){
43278                 this.inKeyMode = true;
43279                 this.selectPrev();
43280             },
43281
43282             "down" : function(e){
43283                 if(!this.isExpanded()){
43284                     this.onTriggerClick();
43285                 }else{
43286                     this.inKeyMode = true;
43287                     this.selectNext();
43288                 }
43289             },
43290
43291             "enter" : function(e){
43292                 this.collapse();
43293                 
43294                 if(this.fireEvent("specialkey", this, e)){
43295                     this.onViewClick(false);
43296                 }
43297                 
43298                 return true;
43299             },
43300
43301             "esc" : function(e){
43302                 this.collapse();
43303             },
43304
43305             "tab" : 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             scope : this,
43316
43317             doRelay : function(foo, bar, hname){
43318                 if(hname == 'down' || this.scope.isExpanded()){
43319                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43320                 }
43321                 return true;
43322             },
43323
43324             forceKeyDown: true
43325         });
43326         
43327         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43328         
43329     },
43330     
43331     initNumberEvent : function(e)
43332     {
43333         this.inputEl().on("keydown" , this.fireKey,  this);
43334         this.inputEl().on("focus", this.onFocus,  this);
43335         this.inputEl().on("blur", this.onBlur,  this);
43336         
43337         this.inputEl().relayEvent('keyup', this);
43338         
43339         if(this.indicator){
43340             this.indicator.addClass('invisible');
43341         }
43342  
43343         this.originalValue = this.getValue();
43344         
43345         if(this.validationEvent == 'keyup'){
43346             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43347             this.inputEl().on('keyup', this.filterValidation, this);
43348         }
43349         else if(this.validationEvent !== false){
43350             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43351         }
43352         
43353         if(this.selectOnFocus){
43354             this.on("focus", this.preFocus, this);
43355             
43356         }
43357         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43358             this.inputEl().on("keypress", this.filterKeys, this);
43359         } else {
43360             this.inputEl().relayEvent('keypress', this);
43361         }
43362         
43363         var allowed = "0123456789";
43364         
43365         if(this.allowDecimals){
43366             allowed += this.decimalSeparator;
43367         }
43368         
43369         if(this.allowNegative){
43370             allowed += "-";
43371         }
43372         
43373         if(this.thousandsDelimiter) {
43374             allowed += ",";
43375         }
43376         
43377         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43378         
43379         var keyPress = function(e){
43380             
43381             var k = e.getKey();
43382             
43383             var c = e.getCharCode();
43384             
43385             if(
43386                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43387                     allowed.indexOf(String.fromCharCode(c)) === -1
43388             ){
43389                 e.stopEvent();
43390                 return;
43391             }
43392             
43393             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43394                 return;
43395             }
43396             
43397             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43398                 e.stopEvent();
43399             }
43400         };
43401         
43402         this.inputEl().on("keypress", keyPress, this);
43403         
43404     },
43405     
43406     onTriggerClick : function(e)
43407     {   
43408         if(this.disabled){
43409             return;
43410         }
43411         
43412         this.page = 0;
43413         this.loadNext = false;
43414         
43415         if(this.isExpanded()){
43416             this.collapse();
43417             return;
43418         }
43419         
43420         this.hasFocus = true;
43421         
43422         if(this.triggerAction == 'all') {
43423             this.doQuery(this.allQuery, true);
43424             return;
43425         }
43426         
43427         this.doQuery(this.getRawValue());
43428     },
43429     
43430     getCurrency : function()
43431     {   
43432         var v = this.currencyEl().getValue();
43433         
43434         return v;
43435     },
43436     
43437     restrictHeight : function()
43438     {
43439         this.list.alignTo(this.currencyEl(), this.listAlign);
43440         this.list.alignTo(this.currencyEl(), this.listAlign);
43441     },
43442     
43443     onViewClick : function(view, doFocus, el, e)
43444     {
43445         var index = this.view.getSelectedIndexes()[0];
43446         
43447         var r = this.store.getAt(index);
43448         
43449         if(r){
43450             this.onSelect(r, index);
43451         }
43452     },
43453     
43454     onSelect : function(record, index){
43455         
43456         if(this.fireEvent('beforeselect', this, record, index) !== false){
43457         
43458             this.setFromCurrencyData(index > -1 ? record.data : false);
43459             
43460             this.collapse();
43461             
43462             this.fireEvent('select', this, record, index);
43463         }
43464     },
43465     
43466     setFromCurrencyData : function(o)
43467     {
43468         var currency = '';
43469         
43470         this.lastCurrency = o;
43471         
43472         if (this.currencyField) {
43473             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43474         } else {
43475             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43476         }
43477         
43478         this.lastSelectionText = currency;
43479         
43480         //setting default currency
43481         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43482             this.setCurrency(this.defaultCurrency);
43483             return;
43484         }
43485         
43486         this.setCurrency(currency);
43487     },
43488     
43489     setFromData : function(o)
43490     {
43491         var c = {};
43492         
43493         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43494         
43495         this.setFromCurrencyData(c);
43496         
43497         var value = '';
43498         
43499         if (this.name) {
43500             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43501         } else {
43502             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43503         }
43504         
43505         this.setValue(value);
43506         
43507     },
43508     
43509     setCurrency : function(v)
43510     {   
43511         this.currencyValue = v;
43512         
43513         if(this.rendered){
43514             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43515             this.validate();
43516         }
43517     },
43518     
43519     setValue : function(v)
43520     {
43521         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43522         
43523         this.value = v;
43524         
43525         if(this.rendered){
43526             
43527             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43528             
43529             this.inputEl().dom.value = (v == '') ? '' :
43530                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43531             
43532             if(!this.allowZero && v === '0') {
43533                 this.hiddenEl().dom.value = '';
43534                 this.inputEl().dom.value = '';
43535             }
43536             
43537             this.validate();
43538         }
43539     },
43540     
43541     getRawValue : function()
43542     {
43543         var v = this.inputEl().getValue();
43544         
43545         return v;
43546     },
43547     
43548     getValue : function()
43549     {
43550         return this.fixPrecision(this.parseValue(this.getRawValue()));
43551     },
43552     
43553     parseValue : function(value)
43554     {
43555         if(this.thousandsDelimiter) {
43556             value += "";
43557             r = new RegExp(",", "g");
43558             value = value.replace(r, "");
43559         }
43560         
43561         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43562         return isNaN(value) ? '' : value;
43563         
43564     },
43565     
43566     fixPrecision : function(value)
43567     {
43568         if(this.thousandsDelimiter) {
43569             value += "";
43570             r = new RegExp(",", "g");
43571             value = value.replace(r, "");
43572         }
43573         
43574         var nan = isNaN(value);
43575         
43576         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43577             return nan ? '' : value;
43578         }
43579         return parseFloat(value).toFixed(this.decimalPrecision);
43580     },
43581     
43582     decimalPrecisionFcn : function(v)
43583     {
43584         return Math.floor(v);
43585     },
43586     
43587     validateValue : function(value)
43588     {
43589         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43590             return false;
43591         }
43592         
43593         var num = this.parseValue(value);
43594         
43595         if(isNaN(num)){
43596             this.markInvalid(String.format(this.nanText, value));
43597             return false;
43598         }
43599         
43600         if(num < this.minValue){
43601             this.markInvalid(String.format(this.minText, this.minValue));
43602             return false;
43603         }
43604         
43605         if(num > this.maxValue){
43606             this.markInvalid(String.format(this.maxText, this.maxValue));
43607             return false;
43608         }
43609         
43610         return true;
43611     },
43612     
43613     validate : function()
43614     {
43615         if(this.disabled || this.allowBlank){
43616             this.markValid();
43617             return true;
43618         }
43619         
43620         var currency = this.getCurrency();
43621         
43622         if(this.validateValue(this.getRawValue()) && currency.length){
43623             this.markValid();
43624             return true;
43625         }
43626         
43627         this.markInvalid();
43628         return false;
43629     },
43630     
43631     getName: function()
43632     {
43633         return this.name;
43634     },
43635     
43636     beforeBlur : function()
43637     {
43638         if(!this.castInt){
43639             return;
43640         }
43641         
43642         var v = this.parseValue(this.getRawValue());
43643         
43644         if(v || v == 0){
43645             this.setValue(v);
43646         }
43647     },
43648     
43649     onBlur : function()
43650     {
43651         this.beforeBlur();
43652         
43653         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43654             //this.el.removeClass(this.focusClass);
43655         }
43656         
43657         this.hasFocus = false;
43658         
43659         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43660             this.validate();
43661         }
43662         
43663         var v = this.getValue();
43664         
43665         if(String(v) !== String(this.startValue)){
43666             this.fireEvent('change', this, v, this.startValue);
43667         }
43668         
43669         this.fireEvent("blur", this);
43670     },
43671     
43672     inputEl : function()
43673     {
43674         return this.el.select('.roo-money-amount-input', true).first();
43675     },
43676     
43677     currencyEl : function()
43678     {
43679         return this.el.select('.roo-money-currency-input', true).first();
43680     },
43681     
43682     hiddenEl : function()
43683     {
43684         return this.el.select('input.hidden-number-input',true).first();
43685     }
43686     
43687 });/**
43688  * @class Roo.bootstrap.BezierSignature
43689  * @extends Roo.bootstrap.Component
43690  * Bootstrap BezierSignature class
43691  * This script refer to:
43692  *    Title: Signature Pad
43693  *    Author: szimek
43694  *    Availability: https://github.com/szimek/signature_pad
43695  *
43696  * @constructor
43697  * Create a new BezierSignature
43698  * @param {Object} config The config object
43699  */
43700
43701 Roo.bootstrap.BezierSignature = function(config){
43702     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43703     this.addEvents({
43704         "resize" : true
43705     });
43706 };
43707
43708 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43709 {
43710      
43711     curve_data: [],
43712     
43713     is_empty: true,
43714     
43715     mouse_btn_down: true,
43716     
43717     /**
43718      * @cfg {int} canvas height
43719      */
43720     canvas_height: '200px',
43721     
43722     /**
43723      * @cfg {float|function} Radius of a single dot.
43724      */ 
43725     dot_size: false,
43726     
43727     /**
43728      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43729      */
43730     min_width: 0.5,
43731     
43732     /**
43733      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43734      */
43735     max_width: 2.5,
43736     
43737     /**
43738      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43739      */
43740     throttle: 16,
43741     
43742     /**
43743      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43744      */
43745     min_distance: 5,
43746     
43747     /**
43748      * @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.
43749      */
43750     bg_color: 'rgba(0, 0, 0, 0)',
43751     
43752     /**
43753      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43754      */
43755     dot_color: 'black',
43756     
43757     /**
43758      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43759      */ 
43760     velocity_filter_weight: 0.7,
43761     
43762     /**
43763      * @cfg {function} Callback when stroke begin. 
43764      */
43765     onBegin: false,
43766     
43767     /**
43768      * @cfg {function} Callback when stroke end.
43769      */
43770     onEnd: false,
43771     
43772     getAutoCreate : function()
43773     {
43774         var cls = 'roo-signature column';
43775         
43776         if(this.cls){
43777             cls += ' ' + this.cls;
43778         }
43779         
43780         var col_sizes = [
43781             'lg',
43782             'md',
43783             'sm',
43784             'xs'
43785         ];
43786         
43787         for(var i = 0; i < col_sizes.length; i++) {
43788             if(this[col_sizes[i]]) {
43789                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43790             }
43791         }
43792         
43793         var cfg = {
43794             tag: 'div',
43795             cls: cls,
43796             cn: [
43797                 {
43798                     tag: 'div',
43799                     cls: 'roo-signature-body',
43800                     cn: [
43801                         {
43802                             tag: 'canvas',
43803                             cls: 'roo-signature-body-canvas',
43804                             height: this.canvas_height,
43805                             width: this.canvas_width
43806                         }
43807                     ]
43808                 },
43809                 {
43810                     tag: 'input',
43811                     type: 'file',
43812                     style: 'display: none'
43813                 }
43814             ]
43815         };
43816         
43817         return cfg;
43818     },
43819     
43820     initEvents: function() 
43821     {
43822         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43823         
43824         var canvas = this.canvasEl();
43825         
43826         // mouse && touch event swapping...
43827         canvas.dom.style.touchAction = 'none';
43828         canvas.dom.style.msTouchAction = 'none';
43829         
43830         this.mouse_btn_down = false;
43831         canvas.on('mousedown', this._handleMouseDown, this);
43832         canvas.on('mousemove', this._handleMouseMove, this);
43833         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43834         
43835         if (window.PointerEvent) {
43836             canvas.on('pointerdown', this._handleMouseDown, this);
43837             canvas.on('pointermove', this._handleMouseMove, this);
43838             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43839         }
43840         
43841         if ('ontouchstart' in window) {
43842             canvas.on('touchstart', this._handleTouchStart, this);
43843             canvas.on('touchmove', this._handleTouchMove, this);
43844             canvas.on('touchend', this._handleTouchEnd, this);
43845         }
43846         
43847         Roo.EventManager.onWindowResize(this.resize, this, true);
43848         
43849         // file input event
43850         this.fileEl().on('change', this.uploadImage, this);
43851         
43852         this.clear();
43853         
43854         this.resize();
43855     },
43856     
43857     resize: function(){
43858         
43859         var canvas = this.canvasEl().dom;
43860         var ctx = this.canvasElCtx();
43861         var img_data = false;
43862         
43863         if(canvas.width > 0) {
43864             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43865         }
43866         // setting canvas width will clean img data
43867         canvas.width = 0;
43868         
43869         var style = window.getComputedStyle ? 
43870             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43871             
43872         var padding_left = parseInt(style.paddingLeft) || 0;
43873         var padding_right = parseInt(style.paddingRight) || 0;
43874         
43875         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43876         
43877         if(img_data) {
43878             ctx.putImageData(img_data, 0, 0);
43879         }
43880     },
43881     
43882     _handleMouseDown: function(e)
43883     {
43884         if (e.browserEvent.which === 1) {
43885             this.mouse_btn_down = true;
43886             this.strokeBegin(e);
43887         }
43888     },
43889     
43890     _handleMouseMove: function (e)
43891     {
43892         if (this.mouse_btn_down) {
43893             this.strokeMoveUpdate(e);
43894         }
43895     },
43896     
43897     _handleMouseUp: function (e)
43898     {
43899         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43900             this.mouse_btn_down = false;
43901             this.strokeEnd(e);
43902         }
43903     },
43904     
43905     _handleTouchStart: function (e) {
43906         
43907         e.preventDefault();
43908         if (e.browserEvent.targetTouches.length === 1) {
43909             // var touch = e.browserEvent.changedTouches[0];
43910             // this.strokeBegin(touch);
43911             
43912              this.strokeBegin(e); // assume e catching the correct xy...
43913         }
43914     },
43915     
43916     _handleTouchMove: function (e) {
43917         e.preventDefault();
43918         // var touch = event.targetTouches[0];
43919         // _this._strokeMoveUpdate(touch);
43920         this.strokeMoveUpdate(e);
43921     },
43922     
43923     _handleTouchEnd: function (e) {
43924         var wasCanvasTouched = e.target === this.canvasEl().dom;
43925         if (wasCanvasTouched) {
43926             e.preventDefault();
43927             // var touch = event.changedTouches[0];
43928             // _this._strokeEnd(touch);
43929             this.strokeEnd(e);
43930         }
43931     },
43932     
43933     reset: function () {
43934         this._lastPoints = [];
43935         this._lastVelocity = 0;
43936         this._lastWidth = (this.min_width + this.max_width) / 2;
43937         this.canvasElCtx().fillStyle = this.dot_color;
43938     },
43939     
43940     strokeMoveUpdate: function(e)
43941     {
43942         this.strokeUpdate(e);
43943         
43944         if (this.throttle) {
43945             this.throttleStroke(this.strokeUpdate, this.throttle);
43946         }
43947         else {
43948             this.strokeUpdate(e);
43949         }
43950     },
43951     
43952     strokeBegin: function(e)
43953     {
43954         var newPointGroup = {
43955             color: this.dot_color,
43956             points: []
43957         };
43958         
43959         if (typeof this.onBegin === 'function') {
43960             this.onBegin(e);
43961         }
43962         
43963         this.curve_data.push(newPointGroup);
43964         this.reset();
43965         this.strokeUpdate(e);
43966     },
43967     
43968     strokeUpdate: function(e)
43969     {
43970         var rect = this.canvasEl().dom.getBoundingClientRect();
43971         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43972         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43973         var lastPoints = lastPointGroup.points;
43974         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43975         var isLastPointTooClose = lastPoint
43976             ? point.distanceTo(lastPoint) <= this.min_distance
43977             : false;
43978         var color = lastPointGroup.color;
43979         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43980             var curve = this.addPoint(point);
43981             if (!lastPoint) {
43982                 this.drawDot({color: color, point: point});
43983             }
43984             else if (curve) {
43985                 this.drawCurve({color: color, curve: curve});
43986             }
43987             lastPoints.push({
43988                 time: point.time,
43989                 x: point.x,
43990                 y: point.y
43991             });
43992         }
43993     },
43994     
43995     strokeEnd: function(e)
43996     {
43997         this.strokeUpdate(e);
43998         if (typeof this.onEnd === 'function') {
43999             this.onEnd(e);
44000         }
44001     },
44002     
44003     addPoint:  function (point) {
44004         var _lastPoints = this._lastPoints;
44005         _lastPoints.push(point);
44006         if (_lastPoints.length > 2) {
44007             if (_lastPoints.length === 3) {
44008                 _lastPoints.unshift(_lastPoints[0]);
44009             }
44010             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44011             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44012             _lastPoints.shift();
44013             return curve;
44014         }
44015         return null;
44016     },
44017     
44018     calculateCurveWidths: function (startPoint, endPoint) {
44019         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44020             (1 - this.velocity_filter_weight) * this._lastVelocity;
44021
44022         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44023         var widths = {
44024             end: newWidth,
44025             start: this._lastWidth
44026         };
44027         
44028         this._lastVelocity = velocity;
44029         this._lastWidth = newWidth;
44030         return widths;
44031     },
44032     
44033     drawDot: function (_a) {
44034         var color = _a.color, point = _a.point;
44035         var ctx = this.canvasElCtx();
44036         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44037         ctx.beginPath();
44038         this.drawCurveSegment(point.x, point.y, width);
44039         ctx.closePath();
44040         ctx.fillStyle = color;
44041         ctx.fill();
44042     },
44043     
44044     drawCurve: function (_a) {
44045         var color = _a.color, curve = _a.curve;
44046         var ctx = this.canvasElCtx();
44047         var widthDelta = curve.endWidth - curve.startWidth;
44048         var drawSteps = Math.floor(curve.length()) * 2;
44049         ctx.beginPath();
44050         ctx.fillStyle = color;
44051         for (var i = 0; i < drawSteps; i += 1) {
44052         var t = i / drawSteps;
44053         var tt = t * t;
44054         var ttt = tt * t;
44055         var u = 1 - t;
44056         var uu = u * u;
44057         var uuu = uu * u;
44058         var x = uuu * curve.startPoint.x;
44059         x += 3 * uu * t * curve.control1.x;
44060         x += 3 * u * tt * curve.control2.x;
44061         x += ttt * curve.endPoint.x;
44062         var y = uuu * curve.startPoint.y;
44063         y += 3 * uu * t * curve.control1.y;
44064         y += 3 * u * tt * curve.control2.y;
44065         y += ttt * curve.endPoint.y;
44066         var width = curve.startWidth + ttt * widthDelta;
44067         this.drawCurveSegment(x, y, width);
44068         }
44069         ctx.closePath();
44070         ctx.fill();
44071     },
44072     
44073     drawCurveSegment: function (x, y, width) {
44074         var ctx = this.canvasElCtx();
44075         ctx.moveTo(x, y);
44076         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44077         this.is_empty = false;
44078     },
44079     
44080     clear: function()
44081     {
44082         var ctx = this.canvasElCtx();
44083         var canvas = this.canvasEl().dom;
44084         ctx.fillStyle = this.bg_color;
44085         ctx.clearRect(0, 0, canvas.width, canvas.height);
44086         ctx.fillRect(0, 0, canvas.width, canvas.height);
44087         this.curve_data = [];
44088         this.reset();
44089         this.is_empty = true;
44090     },
44091     
44092     fileEl: function()
44093     {
44094         return  this.el.select('input',true).first();
44095     },
44096     
44097     canvasEl: function()
44098     {
44099         return this.el.select('canvas',true).first();
44100     },
44101     
44102     canvasElCtx: function()
44103     {
44104         return this.el.select('canvas',true).first().dom.getContext('2d');
44105     },
44106     
44107     getImage: function(type)
44108     {
44109         if(this.is_empty) {
44110             return false;
44111         }
44112         
44113         // encryption ?
44114         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44115     },
44116     
44117     drawFromImage: function(img_src)
44118     {
44119         var img = new Image();
44120         
44121         img.onload = function(){
44122             this.canvasElCtx().drawImage(img, 0, 0);
44123         }.bind(this);
44124         
44125         img.src = img_src;
44126         
44127         this.is_empty = false;
44128     },
44129     
44130     selectImage: function()
44131     {
44132         this.fileEl().dom.click();
44133     },
44134     
44135     uploadImage: function(e)
44136     {
44137         var reader = new FileReader();
44138         
44139         reader.onload = function(e){
44140             var img = new Image();
44141             img.onload = function(){
44142                 this.reset();
44143                 this.canvasElCtx().drawImage(img, 0, 0);
44144             }.bind(this);
44145             img.src = e.target.result;
44146         }.bind(this);
44147         
44148         reader.readAsDataURL(e.target.files[0]);
44149     },
44150     
44151     // Bezier Point Constructor
44152     Point: (function () {
44153         function Point(x, y, time) {
44154             this.x = x;
44155             this.y = y;
44156             this.time = time || Date.now();
44157         }
44158         Point.prototype.distanceTo = function (start) {
44159             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44160         };
44161         Point.prototype.equals = function (other) {
44162             return this.x === other.x && this.y === other.y && this.time === other.time;
44163         };
44164         Point.prototype.velocityFrom = function (start) {
44165             return this.time !== start.time
44166             ? this.distanceTo(start) / (this.time - start.time)
44167             : 0;
44168         };
44169         return Point;
44170     }()),
44171     
44172     
44173     // Bezier Constructor
44174     Bezier: (function () {
44175         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44176             this.startPoint = startPoint;
44177             this.control2 = control2;
44178             this.control1 = control1;
44179             this.endPoint = endPoint;
44180             this.startWidth = startWidth;
44181             this.endWidth = endWidth;
44182         }
44183         Bezier.fromPoints = function (points, widths, scope) {
44184             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44185             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44186             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44187         };
44188         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44189             var dx1 = s1.x - s2.x;
44190             var dy1 = s1.y - s2.y;
44191             var dx2 = s2.x - s3.x;
44192             var dy2 = s2.y - s3.y;
44193             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44194             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44195             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44196             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44197             var dxm = m1.x - m2.x;
44198             var dym = m1.y - m2.y;
44199             var k = l2 / (l1 + l2);
44200             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44201             var tx = s2.x - cm.x;
44202             var ty = s2.y - cm.y;
44203             return {
44204                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44205                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44206             };
44207         };
44208         Bezier.prototype.length = function () {
44209             var steps = 10;
44210             var length = 0;
44211             var px;
44212             var py;
44213             for (var i = 0; i <= steps; i += 1) {
44214                 var t = i / steps;
44215                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44216                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44217                 if (i > 0) {
44218                     var xdiff = cx - px;
44219                     var ydiff = cy - py;
44220                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44221                 }
44222                 px = cx;
44223                 py = cy;
44224             }
44225             return length;
44226         };
44227         Bezier.prototype.point = function (t, start, c1, c2, end) {
44228             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44229             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44230             + (3.0 * c2 * (1.0 - t) * t * t)
44231             + (end * t * t * t);
44232         };
44233         return Bezier;
44234     }()),
44235     
44236     throttleStroke: function(fn, wait) {
44237       if (wait === void 0) { wait = 250; }
44238       var previous = 0;
44239       var timeout = null;
44240       var result;
44241       var storedContext;
44242       var storedArgs;
44243       var later = function () {
44244           previous = Date.now();
44245           timeout = null;
44246           result = fn.apply(storedContext, storedArgs);
44247           if (!timeout) {
44248               storedContext = null;
44249               storedArgs = [];
44250           }
44251       };
44252       return function wrapper() {
44253           var args = [];
44254           for (var _i = 0; _i < arguments.length; _i++) {
44255               args[_i] = arguments[_i];
44256           }
44257           var now = Date.now();
44258           var remaining = wait - (now - previous);
44259           storedContext = this;
44260           storedArgs = args;
44261           if (remaining <= 0 || remaining > wait) {
44262               if (timeout) {
44263                   clearTimeout(timeout);
44264                   timeout = null;
44265               }
44266               previous = now;
44267               result = fn.apply(storedContext, storedArgs);
44268               if (!timeout) {
44269                   storedContext = null;
44270                   storedArgs = [];
44271               }
44272           }
44273           else if (!timeout) {
44274               timeout = window.setTimeout(later, remaining);
44275           }
44276           return result;
44277       };
44278   }
44279   
44280 });
44281
44282  
44283
44284