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' + (this.modal ? '-modal' : ''),
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         if (!this.modal) { 
19762             Roo.bootstrap.Popover.register(this);
19763         }
19764          
19765         
19766         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19767         this.el.enableDisplayMode('block');
19768         this.el.hide();
19769         if (this.over === false && !this.parent()) {
19770             return; 
19771         }
19772         if (this.triggers === false) {
19773             return;
19774         }
19775          
19776         // support parent
19777         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19778         var triggers = this.trigger ? this.trigger.split(' ') : [];
19779         Roo.each(triggers, function(trigger) {
19780         
19781             if (trigger == 'click') {
19782                 on_el.on('click', this.toggle, this);
19783             } else if (trigger != 'manual') {
19784                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19785                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19786       
19787                 on_el.on(eventIn  ,this.enter, this);
19788                 on_el.on(eventOut, this.leave, this);
19789             }
19790         }, this);
19791         
19792     },
19793     
19794     
19795     // private
19796     timeout : null,
19797     hoverState : null,
19798     
19799     toggle : function () {
19800         this.hoverState == 'in' ? this.leave() : this.enter();
19801     },
19802     
19803     enter : function () {
19804         
19805         clearTimeout(this.timeout);
19806     
19807         this.hoverState = 'in';
19808     
19809         if (!this.delay || !this.delay.show) {
19810             this.show();
19811             return;
19812         }
19813         var _t = this;
19814         this.timeout = setTimeout(function () {
19815             if (_t.hoverState == 'in') {
19816                 _t.show();
19817             }
19818         }, this.delay.show)
19819     },
19820     
19821     leave : function() {
19822         clearTimeout(this.timeout);
19823     
19824         this.hoverState = 'out';
19825     
19826         if (!this.delay || !this.delay.hide) {
19827             this.hide();
19828             return;
19829         }
19830         var _t = this;
19831         this.timeout = setTimeout(function () {
19832             if (_t.hoverState == 'out') {
19833                 _t.hide();
19834             }
19835         }, this.delay.hide)
19836     },
19837     /**
19838      * Show the popover
19839      * @param {Roo.Element|string|false} - element to align and point to.
19840      */
19841     show : function (on_el)
19842     {
19843         
19844         on_el = on_el || false; // default to false
19845         if (!on_el) {
19846             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19847                 on_el = this.parent().el;
19848             } else if (this.over) {
19849                 Roo.get(this.over);
19850             }
19851             
19852         }
19853         
19854         if (!this.el) {
19855             this.render(document.body);
19856         }
19857         
19858         
19859         this.el.removeClass([
19860             'fade','top','bottom', 'left', 'right','in',
19861             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19862         ]);
19863         
19864         if (!this.title.length) {
19865             this.el.select('.popover-title',true).hide();
19866         }
19867         
19868         
19869         var placement = typeof this.placement == 'function' ?
19870             this.placement.call(this, this.el, on_el) :
19871             this.placement;
19872             
19873         /*
19874         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19875         
19876         // I think  'auto right' - but 
19877         
19878         var autoPlace = autoToken.test(placement);
19879         if (autoPlace) {
19880             placement = placement.replace(autoToken, '') || 'top';
19881         }
19882         */
19883         
19884         
19885         this.el.show();
19886         this.el.dom.style.display='block';
19887         
19888         //this.el.appendTo(on_el);
19889         
19890         var p = this.getPosition();
19891         var box = this.el.getBox();
19892         
19893         
19894         var align = Roo.bootstrap.Popover.alignment[placement];
19895         this.el.addClass(align[2]);
19896
19897 //        Roo.log(align);
19898
19899         if (on_el) {
19900             this.el.alignTo(on_el, align[0],align[1]);
19901         } else {
19902             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19903             var es = this.el.getSize();
19904             var x = Roo.lib.Dom.getViewWidth()/2;
19905             var y = Roo.lib.Dom.getViewHeight()/2;
19906             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19907             
19908         }
19909
19910         
19911         //var arrow = this.el.select('.arrow',true).first();
19912         //arrow.set(align[2], 
19913         
19914         this.el.addClass('in');
19915         
19916         
19917         if (this.el.hasClass('fade')) {
19918             // fade it?
19919         }
19920         
19921         this.hoverState = 'in';
19922         
19923         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19924         if (this.modal) {
19925             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19926             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19927             this.maskEl.dom.style.display = 'block';
19928             this.maskEl.addClass('show');
19929         }
19930         
19931         
19932         
19933         this.fireEvent('show', this);
19934         
19935     },
19936     hide : function()
19937     {
19938         this.el.setXY([0,0]);
19939         this.el.removeClass('in');
19940         this.el.hide();
19941         this.hoverState = null;
19942         this.maskEl.hide(); // always..
19943         this.fireEvent('hide', this);
19944     }
19945     
19946 });
19947
19948
19949 Roo.apply(Roo.bootstrap.Popover, {
19950
19951     alignment : {
19952         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19953         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19954         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19955         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19956     },
19957     
19958     zIndex : 20001,
19959
19960     clickHander : false,
19961     
19962
19963     onMouseDown : function(e)
19964     {
19965         if (!e.getTarget(".roo-popover")) {
19966             this.hideAll();
19967         }
19968          
19969     },
19970     
19971     popups : [],
19972     
19973     register : function(popup)
19974     {
19975         if (!Roo.bootstrap.Popover.clickHandler) {
19976             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19977         }
19978         // hide other popups.
19979         this.hideAll();
19980         this.popups.push(popup);
19981     },
19982     hideAll : function()
19983     {
19984         this.popups.forEach(function(p) {
19985             p.hide();
19986         });
19987     }
19988
19989 });/*
19990  * - LGPL
19991  *
19992  * Progress
19993  * 
19994  */
19995
19996 /**
19997  * @class Roo.bootstrap.Progress
19998  * @extends Roo.bootstrap.Component
19999  * Bootstrap Progress class
20000  * @cfg {Boolean} striped striped of the progress bar
20001  * @cfg {Boolean} active animated of the progress bar
20002  * 
20003  * 
20004  * @constructor
20005  * Create a new Progress
20006  * @param {Object} config The config object
20007  */
20008
20009 Roo.bootstrap.Progress = function(config){
20010     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20011 };
20012
20013 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20014     
20015     striped : false,
20016     active: false,
20017     
20018     getAutoCreate : function(){
20019         var cfg = {
20020             tag: 'div',
20021             cls: 'progress'
20022         };
20023         
20024         
20025         if(this.striped){
20026             cfg.cls += ' progress-striped';
20027         }
20028       
20029         if(this.active){
20030             cfg.cls += ' active';
20031         }
20032         
20033         
20034         return cfg;
20035     }
20036    
20037 });
20038
20039  
20040
20041  /*
20042  * - LGPL
20043  *
20044  * ProgressBar
20045  * 
20046  */
20047
20048 /**
20049  * @class Roo.bootstrap.ProgressBar
20050  * @extends Roo.bootstrap.Component
20051  * Bootstrap ProgressBar class
20052  * @cfg {Number} aria_valuenow aria-value now
20053  * @cfg {Number} aria_valuemin aria-value min
20054  * @cfg {Number} aria_valuemax aria-value max
20055  * @cfg {String} label label for the progress bar
20056  * @cfg {String} panel (success | info | warning | danger )
20057  * @cfg {String} role role of the progress bar
20058  * @cfg {String} sr_only text
20059  * 
20060  * 
20061  * @constructor
20062  * Create a new ProgressBar
20063  * @param {Object} config The config object
20064  */
20065
20066 Roo.bootstrap.ProgressBar = function(config){
20067     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20068 };
20069
20070 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20071     
20072     aria_valuenow : 0,
20073     aria_valuemin : 0,
20074     aria_valuemax : 100,
20075     label : false,
20076     panel : false,
20077     role : false,
20078     sr_only: false,
20079     
20080     getAutoCreate : function()
20081     {
20082         
20083         var cfg = {
20084             tag: 'div',
20085             cls: 'progress-bar',
20086             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20087         };
20088         
20089         if(this.sr_only){
20090             cfg.cn = {
20091                 tag: 'span',
20092                 cls: 'sr-only',
20093                 html: this.sr_only
20094             }
20095         }
20096         
20097         if(this.role){
20098             cfg.role = this.role;
20099         }
20100         
20101         if(this.aria_valuenow){
20102             cfg['aria-valuenow'] = this.aria_valuenow;
20103         }
20104         
20105         if(this.aria_valuemin){
20106             cfg['aria-valuemin'] = this.aria_valuemin;
20107         }
20108         
20109         if(this.aria_valuemax){
20110             cfg['aria-valuemax'] = this.aria_valuemax;
20111         }
20112         
20113         if(this.label && !this.sr_only){
20114             cfg.html = this.label;
20115         }
20116         
20117         if(this.panel){
20118             cfg.cls += ' progress-bar-' + this.panel;
20119         }
20120         
20121         return cfg;
20122     },
20123     
20124     update : function(aria_valuenow)
20125     {
20126         this.aria_valuenow = aria_valuenow;
20127         
20128         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20129     }
20130    
20131 });
20132
20133  
20134
20135  /*
20136  * - LGPL
20137  *
20138  * column
20139  * 
20140  */
20141
20142 /**
20143  * @class Roo.bootstrap.TabGroup
20144  * @extends Roo.bootstrap.Column
20145  * Bootstrap Column class
20146  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20147  * @cfg {Boolean} carousel true to make the group behave like a carousel
20148  * @cfg {Boolean} bullets show bullets for the panels
20149  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20150  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20151  * @cfg {Boolean} showarrow (true|false) show arrow default true
20152  * 
20153  * @constructor
20154  * Create a new TabGroup
20155  * @param {Object} config The config object
20156  */
20157
20158 Roo.bootstrap.TabGroup = function(config){
20159     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20160     if (!this.navId) {
20161         this.navId = Roo.id();
20162     }
20163     this.tabs = [];
20164     Roo.bootstrap.TabGroup.register(this);
20165     
20166 };
20167
20168 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20169     
20170     carousel : false,
20171     transition : false,
20172     bullets : 0,
20173     timer : 0,
20174     autoslide : false,
20175     slideFn : false,
20176     slideOnTouch : false,
20177     showarrow : true,
20178     
20179     getAutoCreate : function()
20180     {
20181         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20182         
20183         cfg.cls += ' tab-content';
20184         
20185         if (this.carousel) {
20186             cfg.cls += ' carousel slide';
20187             
20188             cfg.cn = [{
20189                cls : 'carousel-inner',
20190                cn : []
20191             }];
20192         
20193             if(this.bullets  && !Roo.isTouch){
20194                 
20195                 var bullets = {
20196                     cls : 'carousel-bullets',
20197                     cn : []
20198                 };
20199                
20200                 if(this.bullets_cls){
20201                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20202                 }
20203                 
20204                 bullets.cn.push({
20205                     cls : 'clear'
20206                 });
20207                 
20208                 cfg.cn[0].cn.push(bullets);
20209             }
20210             
20211             if(this.showarrow){
20212                 cfg.cn[0].cn.push({
20213                     tag : 'div',
20214                     class : 'carousel-arrow',
20215                     cn : [
20216                         {
20217                             tag : 'div',
20218                             class : 'carousel-prev',
20219                             cn : [
20220                                 {
20221                                     tag : 'i',
20222                                     class : 'fa fa-chevron-left'
20223                                 }
20224                             ]
20225                         },
20226                         {
20227                             tag : 'div',
20228                             class : 'carousel-next',
20229                             cn : [
20230                                 {
20231                                     tag : 'i',
20232                                     class : 'fa fa-chevron-right'
20233                                 }
20234                             ]
20235                         }
20236                     ]
20237                 });
20238             }
20239             
20240         }
20241         
20242         return cfg;
20243     },
20244     
20245     initEvents:  function()
20246     {
20247 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20248 //            this.el.on("touchstart", this.onTouchStart, this);
20249 //        }
20250         
20251         if(this.autoslide){
20252             var _this = this;
20253             
20254             this.slideFn = window.setInterval(function() {
20255                 _this.showPanelNext();
20256             }, this.timer);
20257         }
20258         
20259         if(this.showarrow){
20260             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20261             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20262         }
20263         
20264         
20265     },
20266     
20267 //    onTouchStart : function(e, el, o)
20268 //    {
20269 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20270 //            return;
20271 //        }
20272 //        
20273 //        this.showPanelNext();
20274 //    },
20275     
20276     
20277     getChildContainer : function()
20278     {
20279         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20280     },
20281     
20282     /**
20283     * register a Navigation item
20284     * @param {Roo.bootstrap.NavItem} the navitem to add
20285     */
20286     register : function(item)
20287     {
20288         this.tabs.push( item);
20289         item.navId = this.navId; // not really needed..
20290         this.addBullet();
20291     
20292     },
20293     
20294     getActivePanel : function()
20295     {
20296         var r = false;
20297         Roo.each(this.tabs, function(t) {
20298             if (t.active) {
20299                 r = t;
20300                 return false;
20301             }
20302             return null;
20303         });
20304         return r;
20305         
20306     },
20307     getPanelByName : function(n)
20308     {
20309         var r = false;
20310         Roo.each(this.tabs, function(t) {
20311             if (t.tabId == n) {
20312                 r = t;
20313                 return false;
20314             }
20315             return null;
20316         });
20317         return r;
20318     },
20319     indexOfPanel : function(p)
20320     {
20321         var r = false;
20322         Roo.each(this.tabs, function(t,i) {
20323             if (t.tabId == p.tabId) {
20324                 r = i;
20325                 return false;
20326             }
20327             return null;
20328         });
20329         return r;
20330     },
20331     /**
20332      * show a specific panel
20333      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20334      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20335      */
20336     showPanel : function (pan)
20337     {
20338         if(this.transition || typeof(pan) == 'undefined'){
20339             Roo.log("waiting for the transitionend");
20340             return false;
20341         }
20342         
20343         if (typeof(pan) == 'number') {
20344             pan = this.tabs[pan];
20345         }
20346         
20347         if (typeof(pan) == 'string') {
20348             pan = this.getPanelByName(pan);
20349         }
20350         
20351         var cur = this.getActivePanel();
20352         
20353         if(!pan || !cur){
20354             Roo.log('pan or acitve pan is undefined');
20355             return false;
20356         }
20357         
20358         if (pan.tabId == this.getActivePanel().tabId) {
20359             return true;
20360         }
20361         
20362         if (false === cur.fireEvent('beforedeactivate')) {
20363             return false;
20364         }
20365         
20366         if(this.bullets > 0 && !Roo.isTouch){
20367             this.setActiveBullet(this.indexOfPanel(pan));
20368         }
20369         
20370         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20371             
20372             //class="carousel-item carousel-item-next carousel-item-left"
20373             
20374             this.transition = true;
20375             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20376             var lr = dir == 'next' ? 'left' : 'right';
20377             pan.el.addClass(dir); // or prev
20378             pan.el.addClass('carousel-item-' + dir); // or prev
20379             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20380             cur.el.addClass(lr); // or right
20381             pan.el.addClass(lr);
20382             cur.el.addClass('carousel-item-' +lr); // or right
20383             pan.el.addClass('carousel-item-' +lr);
20384             
20385             
20386             var _this = this;
20387             cur.el.on('transitionend', function() {
20388                 Roo.log("trans end?");
20389                 
20390                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20391                 pan.setActive(true);
20392                 
20393                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20394                 cur.setActive(false);
20395                 
20396                 _this.transition = false;
20397                 
20398             }, this, { single:  true } );
20399             
20400             return true;
20401         }
20402         
20403         cur.setActive(false);
20404         pan.setActive(true);
20405         
20406         return true;
20407         
20408     },
20409     showPanelNext : function()
20410     {
20411         var i = this.indexOfPanel(this.getActivePanel());
20412         
20413         if (i >= this.tabs.length - 1 && !this.autoslide) {
20414             return;
20415         }
20416         
20417         if (i >= this.tabs.length - 1 && this.autoslide) {
20418             i = -1;
20419         }
20420         
20421         this.showPanel(this.tabs[i+1]);
20422     },
20423     
20424     showPanelPrev : function()
20425     {
20426         var i = this.indexOfPanel(this.getActivePanel());
20427         
20428         if (i  < 1 && !this.autoslide) {
20429             return;
20430         }
20431         
20432         if (i < 1 && this.autoslide) {
20433             i = this.tabs.length;
20434         }
20435         
20436         this.showPanel(this.tabs[i-1]);
20437     },
20438     
20439     
20440     addBullet: function()
20441     {
20442         if(!this.bullets || Roo.isTouch){
20443             return;
20444         }
20445         var ctr = this.el.select('.carousel-bullets',true).first();
20446         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20447         var bullet = ctr.createChild({
20448             cls : 'bullet bullet-' + i
20449         },ctr.dom.lastChild);
20450         
20451         
20452         var _this = this;
20453         
20454         bullet.on('click', (function(e, el, o, ii, t){
20455
20456             e.preventDefault();
20457
20458             this.showPanel(ii);
20459
20460             if(this.autoslide && this.slideFn){
20461                 clearInterval(this.slideFn);
20462                 this.slideFn = window.setInterval(function() {
20463                     _this.showPanelNext();
20464                 }, this.timer);
20465             }
20466
20467         }).createDelegate(this, [i, bullet], true));
20468                 
20469         
20470     },
20471      
20472     setActiveBullet : function(i)
20473     {
20474         if(Roo.isTouch){
20475             return;
20476         }
20477         
20478         Roo.each(this.el.select('.bullet', true).elements, function(el){
20479             el.removeClass('selected');
20480         });
20481
20482         var bullet = this.el.select('.bullet-' + i, true).first();
20483         
20484         if(!bullet){
20485             return;
20486         }
20487         
20488         bullet.addClass('selected');
20489     }
20490     
20491     
20492   
20493 });
20494
20495  
20496
20497  
20498  
20499 Roo.apply(Roo.bootstrap.TabGroup, {
20500     
20501     groups: {},
20502      /**
20503     * register a Navigation Group
20504     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20505     */
20506     register : function(navgrp)
20507     {
20508         this.groups[navgrp.navId] = navgrp;
20509         
20510     },
20511     /**
20512     * fetch a Navigation Group based on the navigation ID
20513     * if one does not exist , it will get created.
20514     * @param {string} the navgroup to add
20515     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20516     */
20517     get: function(navId) {
20518         if (typeof(this.groups[navId]) == 'undefined') {
20519             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20520         }
20521         return this.groups[navId] ;
20522     }
20523     
20524     
20525     
20526 });
20527
20528  /*
20529  * - LGPL
20530  *
20531  * TabPanel
20532  * 
20533  */
20534
20535 /**
20536  * @class Roo.bootstrap.TabPanel
20537  * @extends Roo.bootstrap.Component
20538  * Bootstrap TabPanel class
20539  * @cfg {Boolean} active panel active
20540  * @cfg {String} html panel content
20541  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20542  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20543  * @cfg {String} href click to link..
20544  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20545  * 
20546  * 
20547  * @constructor
20548  * Create a new TabPanel
20549  * @param {Object} config The config object
20550  */
20551
20552 Roo.bootstrap.TabPanel = function(config){
20553     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20554     this.addEvents({
20555         /**
20556              * @event changed
20557              * Fires when the active status changes
20558              * @param {Roo.bootstrap.TabPanel} this
20559              * @param {Boolean} state the new state
20560             
20561          */
20562         'changed': true,
20563         /**
20564              * @event beforedeactivate
20565              * Fires before a tab is de-activated - can be used to do validation on a form.
20566              * @param {Roo.bootstrap.TabPanel} this
20567              * @return {Boolean} false if there is an error
20568             
20569          */
20570         'beforedeactivate': true
20571      });
20572     
20573     this.tabId = this.tabId || Roo.id();
20574   
20575 };
20576
20577 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20578     
20579     active: false,
20580     html: false,
20581     tabId: false,
20582     navId : false,
20583     href : '',
20584     touchSlide : false,
20585     getAutoCreate : function(){
20586         
20587         
20588         var cfg = {
20589             tag: 'div',
20590             // item is needed for carousel - not sure if it has any effect otherwise
20591             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20592             html: this.html || ''
20593         };
20594         
20595         if(this.active){
20596             cfg.cls += ' active';
20597         }
20598         
20599         if(this.tabId){
20600             cfg.tabId = this.tabId;
20601         }
20602         
20603         
20604         
20605         return cfg;
20606     },
20607     
20608     initEvents:  function()
20609     {
20610         var p = this.parent();
20611         
20612         this.navId = this.navId || p.navId;
20613         
20614         if (typeof(this.navId) != 'undefined') {
20615             // not really needed.. but just in case.. parent should be a NavGroup.
20616             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20617             
20618             tg.register(this);
20619             
20620             var i = tg.tabs.length - 1;
20621             
20622             if(this.active && tg.bullets > 0 && i < tg.bullets){
20623                 tg.setActiveBullet(i);
20624             }
20625         }
20626         
20627         this.el.on('click', this.onClick, this);
20628         
20629         if(Roo.isTouch && this.touchSlide){
20630             this.el.on("touchstart", this.onTouchStart, this);
20631             this.el.on("touchmove", this.onTouchMove, this);
20632             this.el.on("touchend", this.onTouchEnd, this);
20633         }
20634         
20635     },
20636     
20637     onRender : function(ct, position)
20638     {
20639         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20640     },
20641     
20642     setActive : function(state)
20643     {
20644         Roo.log("panel - set active " + this.tabId + "=" + state);
20645         
20646         this.active = state;
20647         if (!state) {
20648             this.el.removeClass('active');
20649             
20650         } else  if (!this.el.hasClass('active')) {
20651             this.el.addClass('active');
20652         }
20653         
20654         this.fireEvent('changed', this, state);
20655     },
20656     
20657     onClick : function(e)
20658     {
20659         e.preventDefault();
20660         
20661         if(!this.href.length){
20662             return;
20663         }
20664         
20665         window.location.href = this.href;
20666     },
20667     
20668     startX : 0,
20669     startY : 0,
20670     endX : 0,
20671     endY : 0,
20672     swiping : false,
20673     
20674     onTouchStart : function(e)
20675     {
20676         this.swiping = false;
20677         
20678         this.startX = e.browserEvent.touches[0].clientX;
20679         this.startY = e.browserEvent.touches[0].clientY;
20680     },
20681     
20682     onTouchMove : function(e)
20683     {
20684         this.swiping = true;
20685         
20686         this.endX = e.browserEvent.touches[0].clientX;
20687         this.endY = e.browserEvent.touches[0].clientY;
20688     },
20689     
20690     onTouchEnd : function(e)
20691     {
20692         if(!this.swiping){
20693             this.onClick(e);
20694             return;
20695         }
20696         
20697         var tabGroup = this.parent();
20698         
20699         if(this.endX > this.startX){ // swiping right
20700             tabGroup.showPanelPrev();
20701             return;
20702         }
20703         
20704         if(this.startX > this.endX){ // swiping left
20705             tabGroup.showPanelNext();
20706             return;
20707         }
20708     }
20709     
20710     
20711 });
20712  
20713
20714  
20715
20716  /*
20717  * - LGPL
20718  *
20719  * DateField
20720  * 
20721  */
20722
20723 /**
20724  * @class Roo.bootstrap.DateField
20725  * @extends Roo.bootstrap.Input
20726  * Bootstrap DateField class
20727  * @cfg {Number} weekStart default 0
20728  * @cfg {String} viewMode default empty, (months|years)
20729  * @cfg {String} minViewMode default empty, (months|years)
20730  * @cfg {Number} startDate default -Infinity
20731  * @cfg {Number} endDate default Infinity
20732  * @cfg {Boolean} todayHighlight default false
20733  * @cfg {Boolean} todayBtn default false
20734  * @cfg {Boolean} calendarWeeks default false
20735  * @cfg {Object} daysOfWeekDisabled default empty
20736  * @cfg {Boolean} singleMode default false (true | false)
20737  * 
20738  * @cfg {Boolean} keyboardNavigation default true
20739  * @cfg {String} language default en
20740  * 
20741  * @constructor
20742  * Create a new DateField
20743  * @param {Object} config The config object
20744  */
20745
20746 Roo.bootstrap.DateField = function(config){
20747     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20748      this.addEvents({
20749             /**
20750              * @event show
20751              * Fires when this field show.
20752              * @param {Roo.bootstrap.DateField} this
20753              * @param {Mixed} date The date value
20754              */
20755             show : true,
20756             /**
20757              * @event show
20758              * Fires when this field hide.
20759              * @param {Roo.bootstrap.DateField} this
20760              * @param {Mixed} date The date value
20761              */
20762             hide : true,
20763             /**
20764              * @event select
20765              * Fires when select a date.
20766              * @param {Roo.bootstrap.DateField} this
20767              * @param {Mixed} date The date value
20768              */
20769             select : true,
20770             /**
20771              * @event beforeselect
20772              * Fires when before select a date.
20773              * @param {Roo.bootstrap.DateField} this
20774              * @param {Mixed} date The date value
20775              */
20776             beforeselect : true
20777         });
20778 };
20779
20780 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20781     
20782     /**
20783      * @cfg {String} format
20784      * The default date format string which can be overriden for localization support.  The format must be
20785      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20786      */
20787     format : "m/d/y",
20788     /**
20789      * @cfg {String} altFormats
20790      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20791      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20792      */
20793     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20794     
20795     weekStart : 0,
20796     
20797     viewMode : '',
20798     
20799     minViewMode : '',
20800     
20801     todayHighlight : false,
20802     
20803     todayBtn: false,
20804     
20805     language: 'en',
20806     
20807     keyboardNavigation: true,
20808     
20809     calendarWeeks: false,
20810     
20811     startDate: -Infinity,
20812     
20813     endDate: Infinity,
20814     
20815     daysOfWeekDisabled: [],
20816     
20817     _events: [],
20818     
20819     singleMode : false,
20820     
20821     UTCDate: function()
20822     {
20823         return new Date(Date.UTC.apply(Date, arguments));
20824     },
20825     
20826     UTCToday: function()
20827     {
20828         var today = new Date();
20829         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20830     },
20831     
20832     getDate: function() {
20833             var d = this.getUTCDate();
20834             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20835     },
20836     
20837     getUTCDate: function() {
20838             return this.date;
20839     },
20840     
20841     setDate: function(d) {
20842             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20843     },
20844     
20845     setUTCDate: function(d) {
20846             this.date = d;
20847             this.setValue(this.formatDate(this.date));
20848     },
20849         
20850     onRender: function(ct, position)
20851     {
20852         
20853         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20854         
20855         this.language = this.language || 'en';
20856         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20857         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20858         
20859         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20860         this.format = this.format || 'm/d/y';
20861         this.isInline = false;
20862         this.isInput = true;
20863         this.component = this.el.select('.add-on', true).first() || false;
20864         this.component = (this.component && this.component.length === 0) ? false : this.component;
20865         this.hasInput = this.component && this.inputEl().length;
20866         
20867         if (typeof(this.minViewMode === 'string')) {
20868             switch (this.minViewMode) {
20869                 case 'months':
20870                     this.minViewMode = 1;
20871                     break;
20872                 case 'years':
20873                     this.minViewMode = 2;
20874                     break;
20875                 default:
20876                     this.minViewMode = 0;
20877                     break;
20878             }
20879         }
20880         
20881         if (typeof(this.viewMode === 'string')) {
20882             switch (this.viewMode) {
20883                 case 'months':
20884                     this.viewMode = 1;
20885                     break;
20886                 case 'years':
20887                     this.viewMode = 2;
20888                     break;
20889                 default:
20890                     this.viewMode = 0;
20891                     break;
20892             }
20893         }
20894                 
20895         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20896         
20897 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20898         
20899         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20900         
20901         this.picker().on('mousedown', this.onMousedown, this);
20902         this.picker().on('click', this.onClick, this);
20903         
20904         this.picker().addClass('datepicker-dropdown');
20905         
20906         this.startViewMode = this.viewMode;
20907         
20908         if(this.singleMode){
20909             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20910                 v.setVisibilityMode(Roo.Element.DISPLAY);
20911                 v.hide();
20912             });
20913             
20914             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20915                 v.setStyle('width', '189px');
20916             });
20917         }
20918         
20919         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20920             if(!this.calendarWeeks){
20921                 v.remove();
20922                 return;
20923             }
20924             
20925             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20926             v.attr('colspan', function(i, val){
20927                 return parseInt(val) + 1;
20928             });
20929         });
20930                         
20931         
20932         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20933         
20934         this.setStartDate(this.startDate);
20935         this.setEndDate(this.endDate);
20936         
20937         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20938         
20939         this.fillDow();
20940         this.fillMonths();
20941         this.update();
20942         this.showMode();
20943         
20944         if(this.isInline) {
20945             this.showPopup();
20946         }
20947     },
20948     
20949     picker : function()
20950     {
20951         return this.pickerEl;
20952 //        return this.el.select('.datepicker', true).first();
20953     },
20954     
20955     fillDow: function()
20956     {
20957         var dowCnt = this.weekStart;
20958         
20959         var dow = {
20960             tag: 'tr',
20961             cn: [
20962                 
20963             ]
20964         };
20965         
20966         if(this.calendarWeeks){
20967             dow.cn.push({
20968                 tag: 'th',
20969                 cls: 'cw',
20970                 html: '&nbsp;'
20971             })
20972         }
20973         
20974         while (dowCnt < this.weekStart + 7) {
20975             dow.cn.push({
20976                 tag: 'th',
20977                 cls: 'dow',
20978                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20979             });
20980         }
20981         
20982         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20983     },
20984     
20985     fillMonths: function()
20986     {    
20987         var i = 0;
20988         var months = this.picker().select('>.datepicker-months td', true).first();
20989         
20990         months.dom.innerHTML = '';
20991         
20992         while (i < 12) {
20993             var month = {
20994                 tag: 'span',
20995                 cls: 'month',
20996                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
20997             };
20998             
20999             months.createChild(month);
21000         }
21001         
21002     },
21003     
21004     update: function()
21005     {
21006         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;
21007         
21008         if (this.date < this.startDate) {
21009             this.viewDate = new Date(this.startDate);
21010         } else if (this.date > this.endDate) {
21011             this.viewDate = new Date(this.endDate);
21012         } else {
21013             this.viewDate = new Date(this.date);
21014         }
21015         
21016         this.fill();
21017     },
21018     
21019     fill: function() 
21020     {
21021         var d = new Date(this.viewDate),
21022                 year = d.getUTCFullYear(),
21023                 month = d.getUTCMonth(),
21024                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21025                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21026                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21027                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21028                 currentDate = this.date && this.date.valueOf(),
21029                 today = this.UTCToday();
21030         
21031         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21032         
21033 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21034         
21035 //        this.picker.select('>tfoot th.today').
21036 //                                              .text(dates[this.language].today)
21037 //                                              .toggle(this.todayBtn !== false);
21038     
21039         this.updateNavArrows();
21040         this.fillMonths();
21041                                                 
21042         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21043         
21044         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21045          
21046         prevMonth.setUTCDate(day);
21047         
21048         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21049         
21050         var nextMonth = new Date(prevMonth);
21051         
21052         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21053         
21054         nextMonth = nextMonth.valueOf();
21055         
21056         var fillMonths = false;
21057         
21058         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21059         
21060         while(prevMonth.valueOf() <= nextMonth) {
21061             var clsName = '';
21062             
21063             if (prevMonth.getUTCDay() === this.weekStart) {
21064                 if(fillMonths){
21065                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21066                 }
21067                     
21068                 fillMonths = {
21069                     tag: 'tr',
21070                     cn: []
21071                 };
21072                 
21073                 if(this.calendarWeeks){
21074                     // ISO 8601: First week contains first thursday.
21075                     // ISO also states week starts on Monday, but we can be more abstract here.
21076                     var
21077                     // Start of current week: based on weekstart/current date
21078                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21079                     // Thursday of this week
21080                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21081                     // First Thursday of year, year from thursday
21082                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21083                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21084                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21085                     
21086                     fillMonths.cn.push({
21087                         tag: 'td',
21088                         cls: 'cw',
21089                         html: calWeek
21090                     });
21091                 }
21092             }
21093             
21094             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21095                 clsName += ' old';
21096             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21097                 clsName += ' new';
21098             }
21099             if (this.todayHighlight &&
21100                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21101                 prevMonth.getUTCMonth() == today.getMonth() &&
21102                 prevMonth.getUTCDate() == today.getDate()) {
21103                 clsName += ' today';
21104             }
21105             
21106             if (currentDate && prevMonth.valueOf() === currentDate) {
21107                 clsName += ' active';
21108             }
21109             
21110             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21111                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21112                     clsName += ' disabled';
21113             }
21114             
21115             fillMonths.cn.push({
21116                 tag: 'td',
21117                 cls: 'day ' + clsName,
21118                 html: prevMonth.getDate()
21119             });
21120             
21121             prevMonth.setDate(prevMonth.getDate()+1);
21122         }
21123           
21124         var currentYear = this.date && this.date.getUTCFullYear();
21125         var currentMonth = this.date && this.date.getUTCMonth();
21126         
21127         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21128         
21129         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21130             v.removeClass('active');
21131             
21132             if(currentYear === year && k === currentMonth){
21133                 v.addClass('active');
21134             }
21135             
21136             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21137                 v.addClass('disabled');
21138             }
21139             
21140         });
21141         
21142         
21143         year = parseInt(year/10, 10) * 10;
21144         
21145         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21146         
21147         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21148         
21149         year -= 1;
21150         for (var i = -1; i < 11; i++) {
21151             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21152                 tag: 'span',
21153                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21154                 html: year
21155             });
21156             
21157             year += 1;
21158         }
21159     },
21160     
21161     showMode: function(dir) 
21162     {
21163         if (dir) {
21164             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21165         }
21166         
21167         Roo.each(this.picker().select('>div',true).elements, function(v){
21168             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21169             v.hide();
21170         });
21171         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21172     },
21173     
21174     place: function()
21175     {
21176         if(this.isInline) {
21177             return;
21178         }
21179         
21180         this.picker().removeClass(['bottom', 'top']);
21181         
21182         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21183             /*
21184              * place to the top of element!
21185              *
21186              */
21187             
21188             this.picker().addClass('top');
21189             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21190             
21191             return;
21192         }
21193         
21194         this.picker().addClass('bottom');
21195         
21196         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21197     },
21198     
21199     parseDate : function(value)
21200     {
21201         if(!value || value instanceof Date){
21202             return value;
21203         }
21204         var v = Date.parseDate(value, this.format);
21205         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21206             v = Date.parseDate(value, 'Y-m-d');
21207         }
21208         if(!v && this.altFormats){
21209             if(!this.altFormatsArray){
21210                 this.altFormatsArray = this.altFormats.split("|");
21211             }
21212             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21213                 v = Date.parseDate(value, this.altFormatsArray[i]);
21214             }
21215         }
21216         return v;
21217     },
21218     
21219     formatDate : function(date, fmt)
21220     {   
21221         return (!date || !(date instanceof Date)) ?
21222         date : date.dateFormat(fmt || this.format);
21223     },
21224     
21225     onFocus : function()
21226     {
21227         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21228         this.showPopup();
21229     },
21230     
21231     onBlur : function()
21232     {
21233         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21234         
21235         var d = this.inputEl().getValue();
21236         
21237         this.setValue(d);
21238                 
21239         this.hidePopup();
21240     },
21241     
21242     showPopup : function()
21243     {
21244         this.picker().show();
21245         this.update();
21246         this.place();
21247         
21248         this.fireEvent('showpopup', this, this.date);
21249     },
21250     
21251     hidePopup : function()
21252     {
21253         if(this.isInline) {
21254             return;
21255         }
21256         this.picker().hide();
21257         this.viewMode = this.startViewMode;
21258         this.showMode();
21259         
21260         this.fireEvent('hidepopup', this, this.date);
21261         
21262     },
21263     
21264     onMousedown: function(e)
21265     {
21266         e.stopPropagation();
21267         e.preventDefault();
21268     },
21269     
21270     keyup: function(e)
21271     {
21272         Roo.bootstrap.DateField.superclass.keyup.call(this);
21273         this.update();
21274     },
21275
21276     setValue: function(v)
21277     {
21278         if(this.fireEvent('beforeselect', this, v) !== false){
21279             var d = new Date(this.parseDate(v) ).clearTime();
21280         
21281             if(isNaN(d.getTime())){
21282                 this.date = this.viewDate = '';
21283                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21284                 return;
21285             }
21286
21287             v = this.formatDate(d);
21288
21289             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21290
21291             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21292
21293             this.update();
21294
21295             this.fireEvent('select', this, this.date);
21296         }
21297     },
21298     
21299     getValue: function()
21300     {
21301         return this.formatDate(this.date);
21302     },
21303     
21304     fireKey: function(e)
21305     {
21306         if (!this.picker().isVisible()){
21307             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21308                 this.showPopup();
21309             }
21310             return;
21311         }
21312         
21313         var dateChanged = false,
21314         dir, day, month,
21315         newDate, newViewDate;
21316         
21317         switch(e.keyCode){
21318             case 27: // escape
21319                 this.hidePopup();
21320                 e.preventDefault();
21321                 break;
21322             case 37: // left
21323             case 39: // right
21324                 if (!this.keyboardNavigation) {
21325                     break;
21326                 }
21327                 dir = e.keyCode == 37 ? -1 : 1;
21328                 
21329                 if (e.ctrlKey){
21330                     newDate = this.moveYear(this.date, dir);
21331                     newViewDate = this.moveYear(this.viewDate, dir);
21332                 } else if (e.shiftKey){
21333                     newDate = this.moveMonth(this.date, dir);
21334                     newViewDate = this.moveMonth(this.viewDate, dir);
21335                 } else {
21336                     newDate = new Date(this.date);
21337                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21338                     newViewDate = new Date(this.viewDate);
21339                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21340                 }
21341                 if (this.dateWithinRange(newDate)){
21342                     this.date = newDate;
21343                     this.viewDate = newViewDate;
21344                     this.setValue(this.formatDate(this.date));
21345 //                    this.update();
21346                     e.preventDefault();
21347                     dateChanged = true;
21348                 }
21349                 break;
21350             case 38: // up
21351             case 40: // down
21352                 if (!this.keyboardNavigation) {
21353                     break;
21354                 }
21355                 dir = e.keyCode == 38 ? -1 : 1;
21356                 if (e.ctrlKey){
21357                     newDate = this.moveYear(this.date, dir);
21358                     newViewDate = this.moveYear(this.viewDate, dir);
21359                 } else if (e.shiftKey){
21360                     newDate = this.moveMonth(this.date, dir);
21361                     newViewDate = this.moveMonth(this.viewDate, dir);
21362                 } else {
21363                     newDate = new Date(this.date);
21364                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21365                     newViewDate = new Date(this.viewDate);
21366                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21367                 }
21368                 if (this.dateWithinRange(newDate)){
21369                     this.date = newDate;
21370                     this.viewDate = newViewDate;
21371                     this.setValue(this.formatDate(this.date));
21372 //                    this.update();
21373                     e.preventDefault();
21374                     dateChanged = true;
21375                 }
21376                 break;
21377             case 13: // enter
21378                 this.setValue(this.formatDate(this.date));
21379                 this.hidePopup();
21380                 e.preventDefault();
21381                 break;
21382             case 9: // tab
21383                 this.setValue(this.formatDate(this.date));
21384                 this.hidePopup();
21385                 break;
21386             case 16: // shift
21387             case 17: // ctrl
21388             case 18: // alt
21389                 break;
21390             default :
21391                 this.hidePopup();
21392                 
21393         }
21394     },
21395     
21396     
21397     onClick: function(e) 
21398     {
21399         e.stopPropagation();
21400         e.preventDefault();
21401         
21402         var target = e.getTarget();
21403         
21404         if(target.nodeName.toLowerCase() === 'i'){
21405             target = Roo.get(target).dom.parentNode;
21406         }
21407         
21408         var nodeName = target.nodeName;
21409         var className = target.className;
21410         var html = target.innerHTML;
21411         //Roo.log(nodeName);
21412         
21413         switch(nodeName.toLowerCase()) {
21414             case 'th':
21415                 switch(className) {
21416                     case 'switch':
21417                         this.showMode(1);
21418                         break;
21419                     case 'prev':
21420                     case 'next':
21421                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21422                         switch(this.viewMode){
21423                                 case 0:
21424                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21425                                         break;
21426                                 case 1:
21427                                 case 2:
21428                                         this.viewDate = this.moveYear(this.viewDate, dir);
21429                                         break;
21430                         }
21431                         this.fill();
21432                         break;
21433                     case 'today':
21434                         var date = new Date();
21435                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21436 //                        this.fill()
21437                         this.setValue(this.formatDate(this.date));
21438                         
21439                         this.hidePopup();
21440                         break;
21441                 }
21442                 break;
21443             case 'span':
21444                 if (className.indexOf('disabled') < 0) {
21445                     this.viewDate.setUTCDate(1);
21446                     if (className.indexOf('month') > -1) {
21447                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21448                     } else {
21449                         var year = parseInt(html, 10) || 0;
21450                         this.viewDate.setUTCFullYear(year);
21451                         
21452                     }
21453                     
21454                     if(this.singleMode){
21455                         this.setValue(this.formatDate(this.viewDate));
21456                         this.hidePopup();
21457                         return;
21458                     }
21459                     
21460                     this.showMode(-1);
21461                     this.fill();
21462                 }
21463                 break;
21464                 
21465             case 'td':
21466                 //Roo.log(className);
21467                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21468                     var day = parseInt(html, 10) || 1;
21469                     var year = this.viewDate.getUTCFullYear(),
21470                         month = this.viewDate.getUTCMonth();
21471
21472                     if (className.indexOf('old') > -1) {
21473                         if(month === 0 ){
21474                             month = 11;
21475                             year -= 1;
21476                         }else{
21477                             month -= 1;
21478                         }
21479                     } else if (className.indexOf('new') > -1) {
21480                         if (month == 11) {
21481                             month = 0;
21482                             year += 1;
21483                         } else {
21484                             month += 1;
21485                         }
21486                     }
21487                     //Roo.log([year,month,day]);
21488                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21489                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21490 //                    this.fill();
21491                     //Roo.log(this.formatDate(this.date));
21492                     this.setValue(this.formatDate(this.date));
21493                     this.hidePopup();
21494                 }
21495                 break;
21496         }
21497     },
21498     
21499     setStartDate: function(startDate)
21500     {
21501         this.startDate = startDate || -Infinity;
21502         if (this.startDate !== -Infinity) {
21503             this.startDate = this.parseDate(this.startDate);
21504         }
21505         this.update();
21506         this.updateNavArrows();
21507     },
21508
21509     setEndDate: function(endDate)
21510     {
21511         this.endDate = endDate || Infinity;
21512         if (this.endDate !== Infinity) {
21513             this.endDate = this.parseDate(this.endDate);
21514         }
21515         this.update();
21516         this.updateNavArrows();
21517     },
21518     
21519     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21520     {
21521         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21522         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21523             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21524         }
21525         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21526             return parseInt(d, 10);
21527         });
21528         this.update();
21529         this.updateNavArrows();
21530     },
21531     
21532     updateNavArrows: function() 
21533     {
21534         if(this.singleMode){
21535             return;
21536         }
21537         
21538         var d = new Date(this.viewDate),
21539         year = d.getUTCFullYear(),
21540         month = d.getUTCMonth();
21541         
21542         Roo.each(this.picker().select('.prev', true).elements, function(v){
21543             v.show();
21544             switch (this.viewMode) {
21545                 case 0:
21546
21547                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21548                         v.hide();
21549                     }
21550                     break;
21551                 case 1:
21552                 case 2:
21553                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21554                         v.hide();
21555                     }
21556                     break;
21557             }
21558         });
21559         
21560         Roo.each(this.picker().select('.next', true).elements, function(v){
21561             v.show();
21562             switch (this.viewMode) {
21563                 case 0:
21564
21565                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21566                         v.hide();
21567                     }
21568                     break;
21569                 case 1:
21570                 case 2:
21571                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21572                         v.hide();
21573                     }
21574                     break;
21575             }
21576         })
21577     },
21578     
21579     moveMonth: function(date, dir)
21580     {
21581         if (!dir) {
21582             return date;
21583         }
21584         var new_date = new Date(date.valueOf()),
21585         day = new_date.getUTCDate(),
21586         month = new_date.getUTCMonth(),
21587         mag = Math.abs(dir),
21588         new_month, test;
21589         dir = dir > 0 ? 1 : -1;
21590         if (mag == 1){
21591             test = dir == -1
21592             // If going back one month, make sure month is not current month
21593             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21594             ? function(){
21595                 return new_date.getUTCMonth() == month;
21596             }
21597             // If going forward one month, make sure month is as expected
21598             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21599             : function(){
21600                 return new_date.getUTCMonth() != new_month;
21601             };
21602             new_month = month + dir;
21603             new_date.setUTCMonth(new_month);
21604             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21605             if (new_month < 0 || new_month > 11) {
21606                 new_month = (new_month + 12) % 12;
21607             }
21608         } else {
21609             // For magnitudes >1, move one month at a time...
21610             for (var i=0; i<mag; i++) {
21611                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21612                 new_date = this.moveMonth(new_date, dir);
21613             }
21614             // ...then reset the day, keeping it in the new month
21615             new_month = new_date.getUTCMonth();
21616             new_date.setUTCDate(day);
21617             test = function(){
21618                 return new_month != new_date.getUTCMonth();
21619             };
21620         }
21621         // Common date-resetting loop -- if date is beyond end of month, make it
21622         // end of month
21623         while (test()){
21624             new_date.setUTCDate(--day);
21625             new_date.setUTCMonth(new_month);
21626         }
21627         return new_date;
21628     },
21629
21630     moveYear: function(date, dir)
21631     {
21632         return this.moveMonth(date, dir*12);
21633     },
21634
21635     dateWithinRange: function(date)
21636     {
21637         return date >= this.startDate && date <= this.endDate;
21638     },
21639
21640     
21641     remove: function() 
21642     {
21643         this.picker().remove();
21644     },
21645     
21646     validateValue : function(value)
21647     {
21648         if(this.getVisibilityEl().hasClass('hidden')){
21649             return true;
21650         }
21651         
21652         if(value.length < 1)  {
21653             if(this.allowBlank){
21654                 return true;
21655             }
21656             return false;
21657         }
21658         
21659         if(value.length < this.minLength){
21660             return false;
21661         }
21662         if(value.length > this.maxLength){
21663             return false;
21664         }
21665         if(this.vtype){
21666             var vt = Roo.form.VTypes;
21667             if(!vt[this.vtype](value, this)){
21668                 return false;
21669             }
21670         }
21671         if(typeof this.validator == "function"){
21672             var msg = this.validator(value);
21673             if(msg !== true){
21674                 return false;
21675             }
21676         }
21677         
21678         if(this.regex && !this.regex.test(value)){
21679             return false;
21680         }
21681         
21682         if(typeof(this.parseDate(value)) == 'undefined'){
21683             return false;
21684         }
21685         
21686         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21687             return false;
21688         }      
21689         
21690         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21691             return false;
21692         } 
21693         
21694         
21695         return true;
21696     },
21697     
21698     reset : function()
21699     {
21700         this.date = this.viewDate = '';
21701         
21702         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21703     }
21704    
21705 });
21706
21707 Roo.apply(Roo.bootstrap.DateField,  {
21708     
21709     head : {
21710         tag: 'thead',
21711         cn: [
21712         {
21713             tag: 'tr',
21714             cn: [
21715             {
21716                 tag: 'th',
21717                 cls: 'prev',
21718                 html: '<i class="fa fa-arrow-left"/>'
21719             },
21720             {
21721                 tag: 'th',
21722                 cls: 'switch',
21723                 colspan: '5'
21724             },
21725             {
21726                 tag: 'th',
21727                 cls: 'next',
21728                 html: '<i class="fa fa-arrow-right"/>'
21729             }
21730
21731             ]
21732         }
21733         ]
21734     },
21735     
21736     content : {
21737         tag: 'tbody',
21738         cn: [
21739         {
21740             tag: 'tr',
21741             cn: [
21742             {
21743                 tag: 'td',
21744                 colspan: '7'
21745             }
21746             ]
21747         }
21748         ]
21749     },
21750     
21751     footer : {
21752         tag: 'tfoot',
21753         cn: [
21754         {
21755             tag: 'tr',
21756             cn: [
21757             {
21758                 tag: 'th',
21759                 colspan: '7',
21760                 cls: 'today'
21761             }
21762                     
21763             ]
21764         }
21765         ]
21766     },
21767     
21768     dates:{
21769         en: {
21770             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21771             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21772             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21773             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21774             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21775             today: "Today"
21776         }
21777     },
21778     
21779     modes: [
21780     {
21781         clsName: 'days',
21782         navFnc: 'Month',
21783         navStep: 1
21784     },
21785     {
21786         clsName: 'months',
21787         navFnc: 'FullYear',
21788         navStep: 1
21789     },
21790     {
21791         clsName: 'years',
21792         navFnc: 'FullYear',
21793         navStep: 10
21794     }]
21795 });
21796
21797 Roo.apply(Roo.bootstrap.DateField,  {
21798   
21799     template : {
21800         tag: 'div',
21801         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21802         cn: [
21803         {
21804             tag: 'div',
21805             cls: 'datepicker-days',
21806             cn: [
21807             {
21808                 tag: 'table',
21809                 cls: 'table-condensed',
21810                 cn:[
21811                 Roo.bootstrap.DateField.head,
21812                 {
21813                     tag: 'tbody'
21814                 },
21815                 Roo.bootstrap.DateField.footer
21816                 ]
21817             }
21818             ]
21819         },
21820         {
21821             tag: 'div',
21822             cls: 'datepicker-months',
21823             cn: [
21824             {
21825                 tag: 'table',
21826                 cls: 'table-condensed',
21827                 cn:[
21828                 Roo.bootstrap.DateField.head,
21829                 Roo.bootstrap.DateField.content,
21830                 Roo.bootstrap.DateField.footer
21831                 ]
21832             }
21833             ]
21834         },
21835         {
21836             tag: 'div',
21837             cls: 'datepicker-years',
21838             cn: [
21839             {
21840                 tag: 'table',
21841                 cls: 'table-condensed',
21842                 cn:[
21843                 Roo.bootstrap.DateField.head,
21844                 Roo.bootstrap.DateField.content,
21845                 Roo.bootstrap.DateField.footer
21846                 ]
21847             }
21848             ]
21849         }
21850         ]
21851     }
21852 });
21853
21854  
21855
21856  /*
21857  * - LGPL
21858  *
21859  * TimeField
21860  * 
21861  */
21862
21863 /**
21864  * @class Roo.bootstrap.TimeField
21865  * @extends Roo.bootstrap.Input
21866  * Bootstrap DateField class
21867  * 
21868  * 
21869  * @constructor
21870  * Create a new TimeField
21871  * @param {Object} config The config object
21872  */
21873
21874 Roo.bootstrap.TimeField = function(config){
21875     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21876     this.addEvents({
21877             /**
21878              * @event show
21879              * Fires when this field show.
21880              * @param {Roo.bootstrap.DateField} thisthis
21881              * @param {Mixed} date The date value
21882              */
21883             show : true,
21884             /**
21885              * @event show
21886              * Fires when this field hide.
21887              * @param {Roo.bootstrap.DateField} this
21888              * @param {Mixed} date The date value
21889              */
21890             hide : true,
21891             /**
21892              * @event select
21893              * Fires when select a date.
21894              * @param {Roo.bootstrap.DateField} this
21895              * @param {Mixed} date The date value
21896              */
21897             select : true
21898         });
21899 };
21900
21901 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21902     
21903     /**
21904      * @cfg {String} format
21905      * The default time format string which can be overriden for localization support.  The format must be
21906      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21907      */
21908     format : "H:i",
21909        
21910     onRender: function(ct, position)
21911     {
21912         
21913         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21914                 
21915         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21916         
21917         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21918         
21919         this.pop = this.picker().select('>.datepicker-time',true).first();
21920         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21921         
21922         this.picker().on('mousedown', this.onMousedown, this);
21923         this.picker().on('click', this.onClick, this);
21924         
21925         this.picker().addClass('datepicker-dropdown');
21926     
21927         this.fillTime();
21928         this.update();
21929             
21930         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21931         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21932         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21933         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21934         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21935         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21936
21937     },
21938     
21939     fireKey: function(e){
21940         if (!this.picker().isVisible()){
21941             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21942                 this.show();
21943             }
21944             return;
21945         }
21946
21947         e.preventDefault();
21948         
21949         switch(e.keyCode){
21950             case 27: // escape
21951                 this.hide();
21952                 break;
21953             case 37: // left
21954             case 39: // right
21955                 this.onTogglePeriod();
21956                 break;
21957             case 38: // up
21958                 this.onIncrementMinutes();
21959                 break;
21960             case 40: // down
21961                 this.onDecrementMinutes();
21962                 break;
21963             case 13: // enter
21964             case 9: // tab
21965                 this.setTime();
21966                 break;
21967         }
21968     },
21969     
21970     onClick: function(e) {
21971         e.stopPropagation();
21972         e.preventDefault();
21973     },
21974     
21975     picker : function()
21976     {
21977         return this.el.select('.datepicker', true).first();
21978     },
21979     
21980     fillTime: function()
21981     {    
21982         var time = this.pop.select('tbody', true).first();
21983         
21984         time.dom.innerHTML = '';
21985         
21986         time.createChild({
21987             tag: 'tr',
21988             cn: [
21989                 {
21990                     tag: 'td',
21991                     cn: [
21992                         {
21993                             tag: 'a',
21994                             href: '#',
21995                             cls: 'btn',
21996                             cn: [
21997                                 {
21998                                     tag: 'span',
21999                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22000                                 }
22001                             ]
22002                         } 
22003                     ]
22004                 },
22005                 {
22006                     tag: 'td',
22007                     cls: 'separator'
22008                 },
22009                 {
22010                     tag: 'td',
22011                     cn: [
22012                         {
22013                             tag: 'a',
22014                             href: '#',
22015                             cls: 'btn',
22016                             cn: [
22017                                 {
22018                                     tag: 'span',
22019                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22020                                 }
22021                             ]
22022                         }
22023                     ]
22024                 },
22025                 {
22026                     tag: 'td',
22027                     cls: 'separator'
22028                 }
22029             ]
22030         });
22031         
22032         time.createChild({
22033             tag: 'tr',
22034             cn: [
22035                 {
22036                     tag: 'td',
22037                     cn: [
22038                         {
22039                             tag: 'span',
22040                             cls: 'timepicker-hour',
22041                             html: '00'
22042                         }  
22043                     ]
22044                 },
22045                 {
22046                     tag: 'td',
22047                     cls: 'separator',
22048                     html: ':'
22049                 },
22050                 {
22051                     tag: 'td',
22052                     cn: [
22053                         {
22054                             tag: 'span',
22055                             cls: 'timepicker-minute',
22056                             html: '00'
22057                         }  
22058                     ]
22059                 },
22060                 {
22061                     tag: 'td',
22062                     cls: 'separator'
22063                 },
22064                 {
22065                     tag: 'td',
22066                     cn: [
22067                         {
22068                             tag: 'button',
22069                             type: 'button',
22070                             cls: 'btn btn-primary period',
22071                             html: 'AM'
22072                             
22073                         }
22074                     ]
22075                 }
22076             ]
22077         });
22078         
22079         time.createChild({
22080             tag: 'tr',
22081             cn: [
22082                 {
22083                     tag: 'td',
22084                     cn: [
22085                         {
22086                             tag: 'a',
22087                             href: '#',
22088                             cls: 'btn',
22089                             cn: [
22090                                 {
22091                                     tag: 'span',
22092                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22093                                 }
22094                             ]
22095                         }
22096                     ]
22097                 },
22098                 {
22099                     tag: 'td',
22100                     cls: 'separator'
22101                 },
22102                 {
22103                     tag: 'td',
22104                     cn: [
22105                         {
22106                             tag: 'a',
22107                             href: '#',
22108                             cls: 'btn',
22109                             cn: [
22110                                 {
22111                                     tag: 'span',
22112                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22113                                 }
22114                             ]
22115                         }
22116                     ]
22117                 },
22118                 {
22119                     tag: 'td',
22120                     cls: 'separator'
22121                 }
22122             ]
22123         });
22124         
22125     },
22126     
22127     update: function()
22128     {
22129         
22130         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22131         
22132         this.fill();
22133     },
22134     
22135     fill: function() 
22136     {
22137         var hours = this.time.getHours();
22138         var minutes = this.time.getMinutes();
22139         var period = 'AM';
22140         
22141         if(hours > 11){
22142             period = 'PM';
22143         }
22144         
22145         if(hours == 0){
22146             hours = 12;
22147         }
22148         
22149         
22150         if(hours > 12){
22151             hours = hours - 12;
22152         }
22153         
22154         if(hours < 10){
22155             hours = '0' + hours;
22156         }
22157         
22158         if(minutes < 10){
22159             minutes = '0' + minutes;
22160         }
22161         
22162         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22163         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22164         this.pop.select('button', true).first().dom.innerHTML = period;
22165         
22166     },
22167     
22168     place: function()
22169     {   
22170         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22171         
22172         var cls = ['bottom'];
22173         
22174         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22175             cls.pop();
22176             cls.push('top');
22177         }
22178         
22179         cls.push('right');
22180         
22181         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22182             cls.pop();
22183             cls.push('left');
22184         }
22185         
22186         this.picker().addClass(cls.join('-'));
22187         
22188         var _this = this;
22189         
22190         Roo.each(cls, function(c){
22191             if(c == 'bottom'){
22192                 _this.picker().setTop(_this.inputEl().getHeight());
22193                 return;
22194             }
22195             if(c == 'top'){
22196                 _this.picker().setTop(0 - _this.picker().getHeight());
22197                 return;
22198             }
22199             
22200             if(c == 'left'){
22201                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22202                 return;
22203             }
22204             if(c == 'right'){
22205                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22206                 return;
22207             }
22208         });
22209         
22210     },
22211   
22212     onFocus : function()
22213     {
22214         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22215         this.show();
22216     },
22217     
22218     onBlur : function()
22219     {
22220         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22221         this.hide();
22222     },
22223     
22224     show : function()
22225     {
22226         this.picker().show();
22227         this.pop.show();
22228         this.update();
22229         this.place();
22230         
22231         this.fireEvent('show', this, this.date);
22232     },
22233     
22234     hide : function()
22235     {
22236         this.picker().hide();
22237         this.pop.hide();
22238         
22239         this.fireEvent('hide', this, this.date);
22240     },
22241     
22242     setTime : function()
22243     {
22244         this.hide();
22245         this.setValue(this.time.format(this.format));
22246         
22247         this.fireEvent('select', this, this.date);
22248         
22249         
22250     },
22251     
22252     onMousedown: function(e){
22253         e.stopPropagation();
22254         e.preventDefault();
22255     },
22256     
22257     onIncrementHours: function()
22258     {
22259         Roo.log('onIncrementHours');
22260         this.time = this.time.add(Date.HOUR, 1);
22261         this.update();
22262         
22263     },
22264     
22265     onDecrementHours: function()
22266     {
22267         Roo.log('onDecrementHours');
22268         this.time = this.time.add(Date.HOUR, -1);
22269         this.update();
22270     },
22271     
22272     onIncrementMinutes: function()
22273     {
22274         Roo.log('onIncrementMinutes');
22275         this.time = this.time.add(Date.MINUTE, 1);
22276         this.update();
22277     },
22278     
22279     onDecrementMinutes: function()
22280     {
22281         Roo.log('onDecrementMinutes');
22282         this.time = this.time.add(Date.MINUTE, -1);
22283         this.update();
22284     },
22285     
22286     onTogglePeriod: function()
22287     {
22288         Roo.log('onTogglePeriod');
22289         this.time = this.time.add(Date.HOUR, 12);
22290         this.update();
22291     }
22292     
22293    
22294 });
22295
22296 Roo.apply(Roo.bootstrap.TimeField,  {
22297     
22298     content : {
22299         tag: 'tbody',
22300         cn: [
22301             {
22302                 tag: 'tr',
22303                 cn: [
22304                 {
22305                     tag: 'td',
22306                     colspan: '7'
22307                 }
22308                 ]
22309             }
22310         ]
22311     },
22312     
22313     footer : {
22314         tag: 'tfoot',
22315         cn: [
22316             {
22317                 tag: 'tr',
22318                 cn: [
22319                 {
22320                     tag: 'th',
22321                     colspan: '7',
22322                     cls: '',
22323                     cn: [
22324                         {
22325                             tag: 'button',
22326                             cls: 'btn btn-info ok',
22327                             html: 'OK'
22328                         }
22329                     ]
22330                 }
22331
22332                 ]
22333             }
22334         ]
22335     }
22336 });
22337
22338 Roo.apply(Roo.bootstrap.TimeField,  {
22339   
22340     template : {
22341         tag: 'div',
22342         cls: 'datepicker dropdown-menu',
22343         cn: [
22344             {
22345                 tag: 'div',
22346                 cls: 'datepicker-time',
22347                 cn: [
22348                 {
22349                     tag: 'table',
22350                     cls: 'table-condensed',
22351                     cn:[
22352                     Roo.bootstrap.TimeField.content,
22353                     Roo.bootstrap.TimeField.footer
22354                     ]
22355                 }
22356                 ]
22357             }
22358         ]
22359     }
22360 });
22361
22362  
22363
22364  /*
22365  * - LGPL
22366  *
22367  * MonthField
22368  * 
22369  */
22370
22371 /**
22372  * @class Roo.bootstrap.MonthField
22373  * @extends Roo.bootstrap.Input
22374  * Bootstrap MonthField class
22375  * 
22376  * @cfg {String} language default en
22377  * 
22378  * @constructor
22379  * Create a new MonthField
22380  * @param {Object} config The config object
22381  */
22382
22383 Roo.bootstrap.MonthField = function(config){
22384     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22385     
22386     this.addEvents({
22387         /**
22388          * @event show
22389          * Fires when this field show.
22390          * @param {Roo.bootstrap.MonthField} this
22391          * @param {Mixed} date The date value
22392          */
22393         show : true,
22394         /**
22395          * @event show
22396          * Fires when this field hide.
22397          * @param {Roo.bootstrap.MonthField} this
22398          * @param {Mixed} date The date value
22399          */
22400         hide : true,
22401         /**
22402          * @event select
22403          * Fires when select a date.
22404          * @param {Roo.bootstrap.MonthField} this
22405          * @param {String} oldvalue The old value
22406          * @param {String} newvalue The new value
22407          */
22408         select : true
22409     });
22410 };
22411
22412 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22413     
22414     onRender: function(ct, position)
22415     {
22416         
22417         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22418         
22419         this.language = this.language || 'en';
22420         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22421         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22422         
22423         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22424         this.isInline = false;
22425         this.isInput = true;
22426         this.component = this.el.select('.add-on', true).first() || false;
22427         this.component = (this.component && this.component.length === 0) ? false : this.component;
22428         this.hasInput = this.component && this.inputEL().length;
22429         
22430         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22431         
22432         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22433         
22434         this.picker().on('mousedown', this.onMousedown, this);
22435         this.picker().on('click', this.onClick, this);
22436         
22437         this.picker().addClass('datepicker-dropdown');
22438         
22439         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22440             v.setStyle('width', '189px');
22441         });
22442         
22443         this.fillMonths();
22444         
22445         this.update();
22446         
22447         if(this.isInline) {
22448             this.show();
22449         }
22450         
22451     },
22452     
22453     setValue: function(v, suppressEvent)
22454     {   
22455         var o = this.getValue();
22456         
22457         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22458         
22459         this.update();
22460
22461         if(suppressEvent !== true){
22462             this.fireEvent('select', this, o, v);
22463         }
22464         
22465     },
22466     
22467     getValue: function()
22468     {
22469         return this.value;
22470     },
22471     
22472     onClick: function(e) 
22473     {
22474         e.stopPropagation();
22475         e.preventDefault();
22476         
22477         var target = e.getTarget();
22478         
22479         if(target.nodeName.toLowerCase() === 'i'){
22480             target = Roo.get(target).dom.parentNode;
22481         }
22482         
22483         var nodeName = target.nodeName;
22484         var className = target.className;
22485         var html = target.innerHTML;
22486         
22487         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22488             return;
22489         }
22490         
22491         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22492         
22493         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22494         
22495         this.hide();
22496                         
22497     },
22498     
22499     picker : function()
22500     {
22501         return this.pickerEl;
22502     },
22503     
22504     fillMonths: function()
22505     {    
22506         var i = 0;
22507         var months = this.picker().select('>.datepicker-months td', true).first();
22508         
22509         months.dom.innerHTML = '';
22510         
22511         while (i < 12) {
22512             var month = {
22513                 tag: 'span',
22514                 cls: 'month',
22515                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22516             };
22517             
22518             months.createChild(month);
22519         }
22520         
22521     },
22522     
22523     update: function()
22524     {
22525         var _this = this;
22526         
22527         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22528             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22529         }
22530         
22531         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22532             e.removeClass('active');
22533             
22534             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22535                 e.addClass('active');
22536             }
22537         })
22538     },
22539     
22540     place: function()
22541     {
22542         if(this.isInline) {
22543             return;
22544         }
22545         
22546         this.picker().removeClass(['bottom', 'top']);
22547         
22548         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22549             /*
22550              * place to the top of element!
22551              *
22552              */
22553             
22554             this.picker().addClass('top');
22555             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22556             
22557             return;
22558         }
22559         
22560         this.picker().addClass('bottom');
22561         
22562         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22563     },
22564     
22565     onFocus : function()
22566     {
22567         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22568         this.show();
22569     },
22570     
22571     onBlur : function()
22572     {
22573         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22574         
22575         var d = this.inputEl().getValue();
22576         
22577         this.setValue(d);
22578                 
22579         this.hide();
22580     },
22581     
22582     show : function()
22583     {
22584         this.picker().show();
22585         this.picker().select('>.datepicker-months', true).first().show();
22586         this.update();
22587         this.place();
22588         
22589         this.fireEvent('show', this, this.date);
22590     },
22591     
22592     hide : function()
22593     {
22594         if(this.isInline) {
22595             return;
22596         }
22597         this.picker().hide();
22598         this.fireEvent('hide', this, this.date);
22599         
22600     },
22601     
22602     onMousedown: function(e)
22603     {
22604         e.stopPropagation();
22605         e.preventDefault();
22606     },
22607     
22608     keyup: function(e)
22609     {
22610         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22611         this.update();
22612     },
22613
22614     fireKey: function(e)
22615     {
22616         if (!this.picker().isVisible()){
22617             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22618                 this.show();
22619             }
22620             return;
22621         }
22622         
22623         var dir;
22624         
22625         switch(e.keyCode){
22626             case 27: // escape
22627                 this.hide();
22628                 e.preventDefault();
22629                 break;
22630             case 37: // left
22631             case 39: // right
22632                 dir = e.keyCode == 37 ? -1 : 1;
22633                 
22634                 this.vIndex = this.vIndex + dir;
22635                 
22636                 if(this.vIndex < 0){
22637                     this.vIndex = 0;
22638                 }
22639                 
22640                 if(this.vIndex > 11){
22641                     this.vIndex = 11;
22642                 }
22643                 
22644                 if(isNaN(this.vIndex)){
22645                     this.vIndex = 0;
22646                 }
22647                 
22648                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22649                 
22650                 break;
22651             case 38: // up
22652             case 40: // down
22653                 
22654                 dir = e.keyCode == 38 ? -1 : 1;
22655                 
22656                 this.vIndex = this.vIndex + dir * 4;
22657                 
22658                 if(this.vIndex < 0){
22659                     this.vIndex = 0;
22660                 }
22661                 
22662                 if(this.vIndex > 11){
22663                     this.vIndex = 11;
22664                 }
22665                 
22666                 if(isNaN(this.vIndex)){
22667                     this.vIndex = 0;
22668                 }
22669                 
22670                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22671                 break;
22672                 
22673             case 13: // enter
22674                 
22675                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22676                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22677                 }
22678                 
22679                 this.hide();
22680                 e.preventDefault();
22681                 break;
22682             case 9: // tab
22683                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22684                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22685                 }
22686                 this.hide();
22687                 break;
22688             case 16: // shift
22689             case 17: // ctrl
22690             case 18: // alt
22691                 break;
22692             default :
22693                 this.hide();
22694                 
22695         }
22696     },
22697     
22698     remove: function() 
22699     {
22700         this.picker().remove();
22701     }
22702    
22703 });
22704
22705 Roo.apply(Roo.bootstrap.MonthField,  {
22706     
22707     content : {
22708         tag: 'tbody',
22709         cn: [
22710         {
22711             tag: 'tr',
22712             cn: [
22713             {
22714                 tag: 'td',
22715                 colspan: '7'
22716             }
22717             ]
22718         }
22719         ]
22720     },
22721     
22722     dates:{
22723         en: {
22724             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22725             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22726         }
22727     }
22728 });
22729
22730 Roo.apply(Roo.bootstrap.MonthField,  {
22731   
22732     template : {
22733         tag: 'div',
22734         cls: 'datepicker dropdown-menu roo-dynamic',
22735         cn: [
22736             {
22737                 tag: 'div',
22738                 cls: 'datepicker-months',
22739                 cn: [
22740                 {
22741                     tag: 'table',
22742                     cls: 'table-condensed',
22743                     cn:[
22744                         Roo.bootstrap.DateField.content
22745                     ]
22746                 }
22747                 ]
22748             }
22749         ]
22750     }
22751 });
22752
22753  
22754
22755  
22756  /*
22757  * - LGPL
22758  *
22759  * CheckBox
22760  * 
22761  */
22762
22763 /**
22764  * @class Roo.bootstrap.CheckBox
22765  * @extends Roo.bootstrap.Input
22766  * Bootstrap CheckBox class
22767  * 
22768  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22769  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22770  * @cfg {String} boxLabel The text that appears beside the checkbox
22771  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22772  * @cfg {Boolean} checked initnal the element
22773  * @cfg {Boolean} inline inline the element (default false)
22774  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22775  * @cfg {String} tooltip label tooltip
22776  * 
22777  * @constructor
22778  * Create a new CheckBox
22779  * @param {Object} config The config object
22780  */
22781
22782 Roo.bootstrap.CheckBox = function(config){
22783     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22784    
22785     this.addEvents({
22786         /**
22787         * @event check
22788         * Fires when the element is checked or unchecked.
22789         * @param {Roo.bootstrap.CheckBox} this This input
22790         * @param {Boolean} checked The new checked value
22791         */
22792        check : true,
22793        /**
22794         * @event click
22795         * Fires when the element is click.
22796         * @param {Roo.bootstrap.CheckBox} this This input
22797         */
22798        click : true
22799     });
22800     
22801 };
22802
22803 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22804   
22805     inputType: 'checkbox',
22806     inputValue: 1,
22807     valueOff: 0,
22808     boxLabel: false,
22809     checked: false,
22810     weight : false,
22811     inline: false,
22812     tooltip : '',
22813     
22814     // checkbox success does not make any sense really.. 
22815     invalidClass : "",
22816     validClass : "",
22817     
22818     
22819     getAutoCreate : function()
22820     {
22821         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22822         
22823         var id = Roo.id();
22824         
22825         var cfg = {};
22826         
22827         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22828         
22829         if(this.inline){
22830             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22831         }
22832         
22833         var input =  {
22834             tag: 'input',
22835             id : id,
22836             type : this.inputType,
22837             value : this.inputValue,
22838             cls : 'roo-' + this.inputType, //'form-box',
22839             placeholder : this.placeholder || ''
22840             
22841         };
22842         
22843         if(this.inputType != 'radio'){
22844             var hidden =  {
22845                 tag: 'input',
22846                 type : 'hidden',
22847                 cls : 'roo-hidden-value',
22848                 value : this.checked ? this.inputValue : this.valueOff
22849             };
22850         }
22851         
22852             
22853         if (this.weight) { // Validity check?
22854             cfg.cls += " " + this.inputType + "-" + this.weight;
22855         }
22856         
22857         if (this.disabled) {
22858             input.disabled=true;
22859         }
22860         
22861         if(this.checked){
22862             input.checked = this.checked;
22863         }
22864         
22865         if (this.name) {
22866             
22867             input.name = this.name;
22868             
22869             if(this.inputType != 'radio'){
22870                 hidden.name = this.name;
22871                 input.name = '_hidden_' + this.name;
22872             }
22873         }
22874         
22875         if (this.size) {
22876             input.cls += ' input-' + this.size;
22877         }
22878         
22879         var settings=this;
22880         
22881         ['xs','sm','md','lg'].map(function(size){
22882             if (settings[size]) {
22883                 cfg.cls += ' col-' + size + '-' + settings[size];
22884             }
22885         });
22886         
22887         var inputblock = input;
22888          
22889         if (this.before || this.after) {
22890             
22891             inputblock = {
22892                 cls : 'input-group',
22893                 cn :  [] 
22894             };
22895             
22896             if (this.before) {
22897                 inputblock.cn.push({
22898                     tag :'span',
22899                     cls : 'input-group-addon',
22900                     html : this.before
22901                 });
22902             }
22903             
22904             inputblock.cn.push(input);
22905             
22906             if(this.inputType != 'radio'){
22907                 inputblock.cn.push(hidden);
22908             }
22909             
22910             if (this.after) {
22911                 inputblock.cn.push({
22912                     tag :'span',
22913                     cls : 'input-group-addon',
22914                     html : this.after
22915                 });
22916             }
22917             
22918         }
22919         var boxLabelCfg = false;
22920         
22921         if(this.boxLabel){
22922            
22923             boxLabelCfg = {
22924                 tag: 'label',
22925                 //'for': id, // box label is handled by onclick - so no for...
22926                 cls: 'box-label',
22927                 html: this.boxLabel
22928             };
22929             if(this.tooltip){
22930                 boxLabelCfg.tooltip = this.tooltip;
22931             }
22932              
22933         }
22934         
22935         
22936         if (align ==='left' && this.fieldLabel.length) {
22937 //                Roo.log("left and has label");
22938             cfg.cn = [
22939                 {
22940                     tag: 'label',
22941                     'for' :  id,
22942                     cls : 'control-label',
22943                     html : this.fieldLabel
22944                 },
22945                 {
22946                     cls : "", 
22947                     cn: [
22948                         inputblock
22949                     ]
22950                 }
22951             ];
22952             
22953             if (boxLabelCfg) {
22954                 cfg.cn[1].cn.push(boxLabelCfg);
22955             }
22956             
22957             if(this.labelWidth > 12){
22958                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22959             }
22960             
22961             if(this.labelWidth < 13 && this.labelmd == 0){
22962                 this.labelmd = this.labelWidth;
22963             }
22964             
22965             if(this.labellg > 0){
22966                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22967                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22968             }
22969             
22970             if(this.labelmd > 0){
22971                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22972                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22973             }
22974             
22975             if(this.labelsm > 0){
22976                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22977                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22978             }
22979             
22980             if(this.labelxs > 0){
22981                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22982                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22983             }
22984             
22985         } else if ( this.fieldLabel.length) {
22986 //                Roo.log(" label");
22987                 cfg.cn = [
22988                    
22989                     {
22990                         tag: this.boxLabel ? 'span' : 'label',
22991                         'for': id,
22992                         cls: 'control-label box-input-label',
22993                         //cls : 'input-group-addon',
22994                         html : this.fieldLabel
22995                     },
22996                     
22997                     inputblock
22998                     
22999                 ];
23000                 if (boxLabelCfg) {
23001                     cfg.cn.push(boxLabelCfg);
23002                 }
23003
23004         } else {
23005             
23006 //                Roo.log(" no label && no align");
23007                 cfg.cn = [  inputblock ] ;
23008                 if (boxLabelCfg) {
23009                     cfg.cn.push(boxLabelCfg);
23010                 }
23011
23012                 
23013         }
23014         
23015        
23016         
23017         if(this.inputType != 'radio'){
23018             cfg.cn.push(hidden);
23019         }
23020         
23021         return cfg;
23022         
23023     },
23024     
23025     /**
23026      * return the real input element.
23027      */
23028     inputEl: function ()
23029     {
23030         return this.el.select('input.roo-' + this.inputType,true).first();
23031     },
23032     hiddenEl: function ()
23033     {
23034         return this.el.select('input.roo-hidden-value',true).first();
23035     },
23036     
23037     labelEl: function()
23038     {
23039         return this.el.select('label.control-label',true).first();
23040     },
23041     /* depricated... */
23042     
23043     label: function()
23044     {
23045         return this.labelEl();
23046     },
23047     
23048     boxLabelEl: function()
23049     {
23050         return this.el.select('label.box-label',true).first();
23051     },
23052     
23053     initEvents : function()
23054     {
23055 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23056         
23057         this.inputEl().on('click', this.onClick,  this);
23058         
23059         if (this.boxLabel) { 
23060             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23061         }
23062         
23063         this.startValue = this.getValue();
23064         
23065         if(this.groupId){
23066             Roo.bootstrap.CheckBox.register(this);
23067         }
23068     },
23069     
23070     onClick : function(e)
23071     {   
23072         if(this.fireEvent('click', this, e) !== false){
23073             this.setChecked(!this.checked);
23074         }
23075         
23076     },
23077     
23078     setChecked : function(state,suppressEvent)
23079     {
23080         this.startValue = this.getValue();
23081
23082         if(this.inputType == 'radio'){
23083             
23084             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23085                 e.dom.checked = false;
23086             });
23087             
23088             this.inputEl().dom.checked = true;
23089             
23090             this.inputEl().dom.value = this.inputValue;
23091             
23092             if(suppressEvent !== true){
23093                 this.fireEvent('check', this, true);
23094             }
23095             
23096             this.validate();
23097             
23098             return;
23099         }
23100         
23101         this.checked = state;
23102         
23103         this.inputEl().dom.checked = state;
23104         
23105         
23106         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23107         
23108         if(suppressEvent !== true){
23109             this.fireEvent('check', this, state);
23110         }
23111         
23112         this.validate();
23113     },
23114     
23115     getValue : function()
23116     {
23117         if(this.inputType == 'radio'){
23118             return this.getGroupValue();
23119         }
23120         
23121         return this.hiddenEl().dom.value;
23122         
23123     },
23124     
23125     getGroupValue : function()
23126     {
23127         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23128             return '';
23129         }
23130         
23131         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23132     },
23133     
23134     setValue : function(v,suppressEvent)
23135     {
23136         if(this.inputType == 'radio'){
23137             this.setGroupValue(v, suppressEvent);
23138             return;
23139         }
23140         
23141         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23142         
23143         this.validate();
23144     },
23145     
23146     setGroupValue : function(v, suppressEvent)
23147     {
23148         this.startValue = this.getValue();
23149         
23150         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23151             e.dom.checked = false;
23152             
23153             if(e.dom.value == v){
23154                 e.dom.checked = true;
23155             }
23156         });
23157         
23158         if(suppressEvent !== true){
23159             this.fireEvent('check', this, true);
23160         }
23161
23162         this.validate();
23163         
23164         return;
23165     },
23166     
23167     validate : function()
23168     {
23169         if(this.getVisibilityEl().hasClass('hidden')){
23170             return true;
23171         }
23172         
23173         if(
23174                 this.disabled || 
23175                 (this.inputType == 'radio' && this.validateRadio()) ||
23176                 (this.inputType == 'checkbox' && this.validateCheckbox())
23177         ){
23178             this.markValid();
23179             return true;
23180         }
23181         
23182         this.markInvalid();
23183         return false;
23184     },
23185     
23186     validateRadio : function()
23187     {
23188         if(this.getVisibilityEl().hasClass('hidden')){
23189             return true;
23190         }
23191         
23192         if(this.allowBlank){
23193             return true;
23194         }
23195         
23196         var valid = false;
23197         
23198         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23199             if(!e.dom.checked){
23200                 return;
23201             }
23202             
23203             valid = true;
23204             
23205             return false;
23206         });
23207         
23208         return valid;
23209     },
23210     
23211     validateCheckbox : function()
23212     {
23213         if(!this.groupId){
23214             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23215             //return (this.getValue() == this.inputValue) ? true : false;
23216         }
23217         
23218         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23219         
23220         if(!group){
23221             return false;
23222         }
23223         
23224         var r = false;
23225         
23226         for(var i in group){
23227             if(group[i].el.isVisible(true)){
23228                 r = false;
23229                 break;
23230             }
23231             
23232             r = true;
23233         }
23234         
23235         for(var i in group){
23236             if(r){
23237                 break;
23238             }
23239             
23240             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23241         }
23242         
23243         return r;
23244     },
23245     
23246     /**
23247      * Mark this field as valid
23248      */
23249     markValid : function()
23250     {
23251         var _this = this;
23252         
23253         this.fireEvent('valid', this);
23254         
23255         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23256         
23257         if(this.groupId){
23258             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23259         }
23260         
23261         if(label){
23262             label.markValid();
23263         }
23264
23265         if(this.inputType == 'radio'){
23266             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23267                 var fg = e.findParent('.form-group', false, true);
23268                 if (Roo.bootstrap.version == 3) {
23269                     fg.removeClass([_this.invalidClass, _this.validClass]);
23270                     fg.addClass(_this.validClass);
23271                 } else {
23272                     fg.removeClass(['is-valid', 'is-invalid']);
23273                     fg.addClass('is-valid');
23274                 }
23275             });
23276             
23277             return;
23278         }
23279
23280         if(!this.groupId){
23281             var fg = this.el.findParent('.form-group', false, true);
23282             if (Roo.bootstrap.version == 3) {
23283                 fg.removeClass([this.invalidClass, this.validClass]);
23284                 fg.addClass(this.validClass);
23285             } else {
23286                 fg.removeClass(['is-valid', 'is-invalid']);
23287                 fg.addClass('is-valid');
23288             }
23289             return;
23290         }
23291         
23292         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23293         
23294         if(!group){
23295             return;
23296         }
23297         
23298         for(var i in group){
23299             var fg = group[i].el.findParent('.form-group', false, true);
23300             if (Roo.bootstrap.version == 3) {
23301                 fg.removeClass([this.invalidClass, this.validClass]);
23302                 fg.addClass(this.validClass);
23303             } else {
23304                 fg.removeClass(['is-valid', 'is-invalid']);
23305                 fg.addClass('is-valid');
23306             }
23307         }
23308     },
23309     
23310      /**
23311      * Mark this field as invalid
23312      * @param {String} msg The validation message
23313      */
23314     markInvalid : function(msg)
23315     {
23316         if(this.allowBlank){
23317             return;
23318         }
23319         
23320         var _this = this;
23321         
23322         this.fireEvent('invalid', this, msg);
23323         
23324         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23325         
23326         if(this.groupId){
23327             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23328         }
23329         
23330         if(label){
23331             label.markInvalid();
23332         }
23333             
23334         if(this.inputType == 'radio'){
23335             
23336             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23337                 var fg = e.findParent('.form-group', false, true);
23338                 if (Roo.bootstrap.version == 3) {
23339                     fg.removeClass([_this.invalidClass, _this.validClass]);
23340                     fg.addClass(_this.invalidClass);
23341                 } else {
23342                     fg.removeClass(['is-invalid', 'is-valid']);
23343                     fg.addClass('is-invalid');
23344                 }
23345             });
23346             
23347             return;
23348         }
23349         
23350         if(!this.groupId){
23351             var fg = this.el.findParent('.form-group', false, true);
23352             if (Roo.bootstrap.version == 3) {
23353                 fg.removeClass([_this.invalidClass, _this.validClass]);
23354                 fg.addClass(_this.invalidClass);
23355             } else {
23356                 fg.removeClass(['is-invalid', 'is-valid']);
23357                 fg.addClass('is-invalid');
23358             }
23359             return;
23360         }
23361         
23362         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23363         
23364         if(!group){
23365             return;
23366         }
23367         
23368         for(var i in group){
23369             var fg = group[i].el.findParent('.form-group', false, true);
23370             if (Roo.bootstrap.version == 3) {
23371                 fg.removeClass([_this.invalidClass, _this.validClass]);
23372                 fg.addClass(_this.invalidClass);
23373             } else {
23374                 fg.removeClass(['is-invalid', 'is-valid']);
23375                 fg.addClass('is-invalid');
23376             }
23377         }
23378         
23379     },
23380     
23381     clearInvalid : function()
23382     {
23383         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23384         
23385         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23386         
23387         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23388         
23389         if (label && label.iconEl) {
23390             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23391             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23392         }
23393     },
23394     
23395     disable : function()
23396     {
23397         if(this.inputType != 'radio'){
23398             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23399             return;
23400         }
23401         
23402         var _this = this;
23403         
23404         if(this.rendered){
23405             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23406                 _this.getActionEl().addClass(this.disabledClass);
23407                 e.dom.disabled = true;
23408             });
23409         }
23410         
23411         this.disabled = true;
23412         this.fireEvent("disable", this);
23413         return this;
23414     },
23415
23416     enable : function()
23417     {
23418         if(this.inputType != 'radio'){
23419             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23420             return;
23421         }
23422         
23423         var _this = this;
23424         
23425         if(this.rendered){
23426             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23427                 _this.getActionEl().removeClass(this.disabledClass);
23428                 e.dom.disabled = false;
23429             });
23430         }
23431         
23432         this.disabled = false;
23433         this.fireEvent("enable", this);
23434         return this;
23435     },
23436     
23437     setBoxLabel : function(v)
23438     {
23439         this.boxLabel = v;
23440         
23441         if(this.rendered){
23442             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23443         }
23444     }
23445
23446 });
23447
23448 Roo.apply(Roo.bootstrap.CheckBox, {
23449     
23450     groups: {},
23451     
23452      /**
23453     * register a CheckBox Group
23454     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23455     */
23456     register : function(checkbox)
23457     {
23458         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23459             this.groups[checkbox.groupId] = {};
23460         }
23461         
23462         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23463             return;
23464         }
23465         
23466         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23467         
23468     },
23469     /**
23470     * fetch a CheckBox Group based on the group ID
23471     * @param {string} the group ID
23472     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23473     */
23474     get: function(groupId) {
23475         if (typeof(this.groups[groupId]) == 'undefined') {
23476             return false;
23477         }
23478         
23479         return this.groups[groupId] ;
23480     }
23481     
23482     
23483 });
23484 /*
23485  * - LGPL
23486  *
23487  * RadioItem
23488  * 
23489  */
23490
23491 /**
23492  * @class Roo.bootstrap.Radio
23493  * @extends Roo.bootstrap.Component
23494  * Bootstrap Radio class
23495  * @cfg {String} boxLabel - the label associated
23496  * @cfg {String} value - the value of radio
23497  * 
23498  * @constructor
23499  * Create a new Radio
23500  * @param {Object} config The config object
23501  */
23502 Roo.bootstrap.Radio = function(config){
23503     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23504     
23505 };
23506
23507 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23508     
23509     boxLabel : '',
23510     
23511     value : '',
23512     
23513     getAutoCreate : function()
23514     {
23515         var cfg = {
23516             tag : 'div',
23517             cls : 'form-group radio',
23518             cn : [
23519                 {
23520                     tag : 'label',
23521                     cls : 'box-label',
23522                     html : this.boxLabel
23523                 }
23524             ]
23525         };
23526         
23527         return cfg;
23528     },
23529     
23530     initEvents : function() 
23531     {
23532         this.parent().register(this);
23533         
23534         this.el.on('click', this.onClick, this);
23535         
23536     },
23537     
23538     onClick : function(e)
23539     {
23540         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23541             this.setChecked(true);
23542         }
23543     },
23544     
23545     setChecked : function(state, suppressEvent)
23546     {
23547         this.parent().setValue(this.value, suppressEvent);
23548         
23549     },
23550     
23551     setBoxLabel : function(v)
23552     {
23553         this.boxLabel = v;
23554         
23555         if(this.rendered){
23556             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23557         }
23558     }
23559     
23560 });
23561  
23562
23563  /*
23564  * - LGPL
23565  *
23566  * Input
23567  * 
23568  */
23569
23570 /**
23571  * @class Roo.bootstrap.SecurePass
23572  * @extends Roo.bootstrap.Input
23573  * Bootstrap SecurePass class
23574  *
23575  * 
23576  * @constructor
23577  * Create a new SecurePass
23578  * @param {Object} config The config object
23579  */
23580  
23581 Roo.bootstrap.SecurePass = function (config) {
23582     // these go here, so the translation tool can replace them..
23583     this.errors = {
23584         PwdEmpty: "Please type a password, and then retype it to confirm.",
23585         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23586         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23587         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23588         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23589         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23590         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23591         TooWeak: "Your password is Too Weak."
23592     },
23593     this.meterLabel = "Password strength:";
23594     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23595     this.meterClass = [
23596         "roo-password-meter-tooweak", 
23597         "roo-password-meter-weak", 
23598         "roo-password-meter-medium", 
23599         "roo-password-meter-strong", 
23600         "roo-password-meter-grey"
23601     ];
23602     
23603     this.errors = {};
23604     
23605     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23606 }
23607
23608 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23609     /**
23610      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23611      * {
23612      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23613      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23614      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23615      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23616      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23617      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23618      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23619      * })
23620      */
23621     // private
23622     
23623     meterWidth: 300,
23624     errorMsg :'',    
23625     errors: false,
23626     imageRoot: '/',
23627     /**
23628      * @cfg {String/Object} Label for the strength meter (defaults to
23629      * 'Password strength:')
23630      */
23631     // private
23632     meterLabel: '',
23633     /**
23634      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23635      * ['Weak', 'Medium', 'Strong'])
23636      */
23637     // private    
23638     pwdStrengths: false,    
23639     // private
23640     strength: 0,
23641     // private
23642     _lastPwd: null,
23643     // private
23644     kCapitalLetter: 0,
23645     kSmallLetter: 1,
23646     kDigit: 2,
23647     kPunctuation: 3,
23648     
23649     insecure: false,
23650     // private
23651     initEvents: function ()
23652     {
23653         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23654
23655         if (this.el.is('input[type=password]') && Roo.isSafari) {
23656             this.el.on('keydown', this.SafariOnKeyDown, this);
23657         }
23658
23659         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23660     },
23661     // private
23662     onRender: function (ct, position)
23663     {
23664         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23665         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23666         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23667
23668         this.trigger.createChild({
23669                    cn: [
23670                     {
23671                     //id: 'PwdMeter',
23672                     tag: 'div',
23673                     cls: 'roo-password-meter-grey col-xs-12',
23674                     style: {
23675                         //width: 0,
23676                         //width: this.meterWidth + 'px'                                                
23677                         }
23678                     },
23679                     {                            
23680                          cls: 'roo-password-meter-text'                          
23681                     }
23682                 ]            
23683         });
23684
23685          
23686         if (this.hideTrigger) {
23687             this.trigger.setDisplayed(false);
23688         }
23689         this.setSize(this.width || '', this.height || '');
23690     },
23691     // private
23692     onDestroy: function ()
23693     {
23694         if (this.trigger) {
23695             this.trigger.removeAllListeners();
23696             this.trigger.remove();
23697         }
23698         if (this.wrap) {
23699             this.wrap.remove();
23700         }
23701         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23702     },
23703     // private
23704     checkStrength: function ()
23705     {
23706         var pwd = this.inputEl().getValue();
23707         if (pwd == this._lastPwd) {
23708             return;
23709         }
23710
23711         var strength;
23712         if (this.ClientSideStrongPassword(pwd)) {
23713             strength = 3;
23714         } else if (this.ClientSideMediumPassword(pwd)) {
23715             strength = 2;
23716         } else if (this.ClientSideWeakPassword(pwd)) {
23717             strength = 1;
23718         } else {
23719             strength = 0;
23720         }
23721         
23722         Roo.log('strength1: ' + strength);
23723         
23724         //var pm = this.trigger.child('div/div/div').dom;
23725         var pm = this.trigger.child('div/div');
23726         pm.removeClass(this.meterClass);
23727         pm.addClass(this.meterClass[strength]);
23728                 
23729         
23730         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23731                 
23732         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23733         
23734         this._lastPwd = pwd;
23735     },
23736     reset: function ()
23737     {
23738         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23739         
23740         this._lastPwd = '';
23741         
23742         var pm = this.trigger.child('div/div');
23743         pm.removeClass(this.meterClass);
23744         pm.addClass('roo-password-meter-grey');        
23745         
23746         
23747         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23748         
23749         pt.innerHTML = '';
23750         this.inputEl().dom.type='password';
23751     },
23752     // private
23753     validateValue: function (value)
23754     {
23755         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23756             return false;
23757         }
23758         if (value.length == 0) {
23759             if (this.allowBlank) {
23760                 this.clearInvalid();
23761                 return true;
23762             }
23763
23764             this.markInvalid(this.errors.PwdEmpty);
23765             this.errorMsg = this.errors.PwdEmpty;
23766             return false;
23767         }
23768         
23769         if(this.insecure){
23770             return true;
23771         }
23772         
23773         if (!value.match(/[\x21-\x7e]+/)) {
23774             this.markInvalid(this.errors.PwdBadChar);
23775             this.errorMsg = this.errors.PwdBadChar;
23776             return false;
23777         }
23778         if (value.length < 6) {
23779             this.markInvalid(this.errors.PwdShort);
23780             this.errorMsg = this.errors.PwdShort;
23781             return false;
23782         }
23783         if (value.length > 16) {
23784             this.markInvalid(this.errors.PwdLong);
23785             this.errorMsg = this.errors.PwdLong;
23786             return false;
23787         }
23788         var strength;
23789         if (this.ClientSideStrongPassword(value)) {
23790             strength = 3;
23791         } else if (this.ClientSideMediumPassword(value)) {
23792             strength = 2;
23793         } else if (this.ClientSideWeakPassword(value)) {
23794             strength = 1;
23795         } else {
23796             strength = 0;
23797         }
23798
23799         
23800         if (strength < 2) {
23801             //this.markInvalid(this.errors.TooWeak);
23802             this.errorMsg = this.errors.TooWeak;
23803             //return false;
23804         }
23805         
23806         
23807         console.log('strength2: ' + strength);
23808         
23809         //var pm = this.trigger.child('div/div/div').dom;
23810         
23811         var pm = this.trigger.child('div/div');
23812         pm.removeClass(this.meterClass);
23813         pm.addClass(this.meterClass[strength]);
23814                 
23815         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23816                 
23817         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23818         
23819         this.errorMsg = ''; 
23820         return true;
23821     },
23822     // private
23823     CharacterSetChecks: function (type)
23824     {
23825         this.type = type;
23826         this.fResult = false;
23827     },
23828     // private
23829     isctype: function (character, type)
23830     {
23831         switch (type) {  
23832             case this.kCapitalLetter:
23833                 if (character >= 'A' && character <= 'Z') {
23834                     return true;
23835                 }
23836                 break;
23837             
23838             case this.kSmallLetter:
23839                 if (character >= 'a' && character <= 'z') {
23840                     return true;
23841                 }
23842                 break;
23843             
23844             case this.kDigit:
23845                 if (character >= '0' && character <= '9') {
23846                     return true;
23847                 }
23848                 break;
23849             
23850             case this.kPunctuation:
23851                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23852                     return true;
23853                 }
23854                 break;
23855             
23856             default:
23857                 return false;
23858         }
23859
23860     },
23861     // private
23862     IsLongEnough: function (pwd, size)
23863     {
23864         return !(pwd == null || isNaN(size) || pwd.length < size);
23865     },
23866     // private
23867     SpansEnoughCharacterSets: function (word, nb)
23868     {
23869         if (!this.IsLongEnough(word, nb))
23870         {
23871             return false;
23872         }
23873
23874         var characterSetChecks = new Array(
23875             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23876             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23877         );
23878         
23879         for (var index = 0; index < word.length; ++index) {
23880             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23881                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23882                     characterSetChecks[nCharSet].fResult = true;
23883                     break;
23884                 }
23885             }
23886         }
23887
23888         var nCharSets = 0;
23889         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23890             if (characterSetChecks[nCharSet].fResult) {
23891                 ++nCharSets;
23892             }
23893         }
23894
23895         if (nCharSets < nb) {
23896             return false;
23897         }
23898         return true;
23899     },
23900     // private
23901     ClientSideStrongPassword: function (pwd)
23902     {
23903         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23904     },
23905     // private
23906     ClientSideMediumPassword: function (pwd)
23907     {
23908         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23909     },
23910     // private
23911     ClientSideWeakPassword: function (pwd)
23912     {
23913         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23914     }
23915           
23916 })//<script type="text/javascript">
23917
23918 /*
23919  * Based  Ext JS Library 1.1.1
23920  * Copyright(c) 2006-2007, Ext JS, LLC.
23921  * LGPL
23922  *
23923  */
23924  
23925 /**
23926  * @class Roo.HtmlEditorCore
23927  * @extends Roo.Component
23928  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23929  *
23930  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23931  */
23932
23933 Roo.HtmlEditorCore = function(config){
23934     
23935     
23936     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23937     
23938     
23939     this.addEvents({
23940         /**
23941          * @event initialize
23942          * Fires when the editor is fully initialized (including the iframe)
23943          * @param {Roo.HtmlEditorCore} this
23944          */
23945         initialize: true,
23946         /**
23947          * @event activate
23948          * Fires when the editor is first receives the focus. Any insertion must wait
23949          * until after this event.
23950          * @param {Roo.HtmlEditorCore} this
23951          */
23952         activate: true,
23953          /**
23954          * @event beforesync
23955          * Fires before the textarea is updated with content from the editor iframe. Return false
23956          * to cancel the sync.
23957          * @param {Roo.HtmlEditorCore} this
23958          * @param {String} html
23959          */
23960         beforesync: true,
23961          /**
23962          * @event beforepush
23963          * Fires before the iframe editor is updated with content from the textarea. Return false
23964          * to cancel the push.
23965          * @param {Roo.HtmlEditorCore} this
23966          * @param {String} html
23967          */
23968         beforepush: true,
23969          /**
23970          * @event sync
23971          * Fires when the textarea is updated with content from the editor iframe.
23972          * @param {Roo.HtmlEditorCore} this
23973          * @param {String} html
23974          */
23975         sync: true,
23976          /**
23977          * @event push
23978          * Fires when the iframe editor is updated with content from the textarea.
23979          * @param {Roo.HtmlEditorCore} this
23980          * @param {String} html
23981          */
23982         push: true,
23983         
23984         /**
23985          * @event editorevent
23986          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23987          * @param {Roo.HtmlEditorCore} this
23988          */
23989         editorevent: true
23990         
23991     });
23992     
23993     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
23994     
23995     // defaults : white / black...
23996     this.applyBlacklists();
23997     
23998     
23999     
24000 };
24001
24002
24003 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24004
24005
24006      /**
24007      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24008      */
24009     
24010     owner : false,
24011     
24012      /**
24013      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24014      *                        Roo.resizable.
24015      */
24016     resizable : false,
24017      /**
24018      * @cfg {Number} height (in pixels)
24019      */   
24020     height: 300,
24021    /**
24022      * @cfg {Number} width (in pixels)
24023      */   
24024     width: 500,
24025     
24026     /**
24027      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24028      * 
24029      */
24030     stylesheets: false,
24031     
24032     // id of frame..
24033     frameId: false,
24034     
24035     // private properties
24036     validationEvent : false,
24037     deferHeight: true,
24038     initialized : false,
24039     activated : false,
24040     sourceEditMode : false,
24041     onFocus : Roo.emptyFn,
24042     iframePad:3,
24043     hideMode:'offsets',
24044     
24045     clearUp: true,
24046     
24047     // blacklist + whitelisted elements..
24048     black: false,
24049     white: false,
24050      
24051     bodyCls : '',
24052
24053     /**
24054      * Protected method that will not generally be called directly. It
24055      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24056      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24057      */
24058     getDocMarkup : function(){
24059         // body styles..
24060         var st = '';
24061         
24062         // inherit styels from page...?? 
24063         if (this.stylesheets === false) {
24064             
24065             Roo.get(document.head).select('style').each(function(node) {
24066                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24067             });
24068             
24069             Roo.get(document.head).select('link').each(function(node) { 
24070                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24071             });
24072             
24073         } else if (!this.stylesheets.length) {
24074                 // simple..
24075                 st = '<style type="text/css">' +
24076                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24077                    '</style>';
24078         } else {
24079             for (var i in this.stylesheets) { 
24080                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24081             }
24082             
24083         }
24084         
24085         st +=  '<style type="text/css">' +
24086             'IMG { cursor: pointer } ' +
24087         '</style>';
24088
24089         var cls = 'roo-htmleditor-body';
24090         
24091         if(this.bodyCls.length){
24092             cls += ' ' + this.bodyCls;
24093         }
24094         
24095         return '<html><head>' + st  +
24096             //<style type="text/css">' +
24097             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24098             //'</style>' +
24099             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24100     },
24101
24102     // private
24103     onRender : function(ct, position)
24104     {
24105         var _t = this;
24106         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24107         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24108         
24109         
24110         this.el.dom.style.border = '0 none';
24111         this.el.dom.setAttribute('tabIndex', -1);
24112         this.el.addClass('x-hidden hide');
24113         
24114         
24115         
24116         if(Roo.isIE){ // fix IE 1px bogus margin
24117             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24118         }
24119        
24120         
24121         this.frameId = Roo.id();
24122         
24123          
24124         
24125         var iframe = this.owner.wrap.createChild({
24126             tag: 'iframe',
24127             cls: 'form-control', // bootstrap..
24128             id: this.frameId,
24129             name: this.frameId,
24130             frameBorder : 'no',
24131             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24132         }, this.el
24133         );
24134         
24135         
24136         this.iframe = iframe.dom;
24137
24138          this.assignDocWin();
24139         
24140         this.doc.designMode = 'on';
24141        
24142         this.doc.open();
24143         this.doc.write(this.getDocMarkup());
24144         this.doc.close();
24145
24146         
24147         var task = { // must defer to wait for browser to be ready
24148             run : function(){
24149                 //console.log("run task?" + this.doc.readyState);
24150                 this.assignDocWin();
24151                 if(this.doc.body || this.doc.readyState == 'complete'){
24152                     try {
24153                         this.doc.designMode="on";
24154                     } catch (e) {
24155                         return;
24156                     }
24157                     Roo.TaskMgr.stop(task);
24158                     this.initEditor.defer(10, this);
24159                 }
24160             },
24161             interval : 10,
24162             duration: 10000,
24163             scope: this
24164         };
24165         Roo.TaskMgr.start(task);
24166
24167     },
24168
24169     // private
24170     onResize : function(w, h)
24171     {
24172          Roo.log('resize: ' +w + ',' + h );
24173         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24174         if(!this.iframe){
24175             return;
24176         }
24177         if(typeof w == 'number'){
24178             
24179             this.iframe.style.width = w + 'px';
24180         }
24181         if(typeof h == 'number'){
24182             
24183             this.iframe.style.height = h + 'px';
24184             if(this.doc){
24185                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24186             }
24187         }
24188         
24189     },
24190
24191     /**
24192      * Toggles the editor between standard and source edit mode.
24193      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24194      */
24195     toggleSourceEdit : function(sourceEditMode){
24196         
24197         this.sourceEditMode = sourceEditMode === true;
24198         
24199         if(this.sourceEditMode){
24200  
24201             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24202             
24203         }else{
24204             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24205             //this.iframe.className = '';
24206             this.deferFocus();
24207         }
24208         //this.setSize(this.owner.wrap.getSize());
24209         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24210     },
24211
24212     
24213   
24214
24215     /**
24216      * Protected method that will not generally be called directly. If you need/want
24217      * custom HTML cleanup, this is the method you should override.
24218      * @param {String} html The HTML to be cleaned
24219      * return {String} The cleaned HTML
24220      */
24221     cleanHtml : function(html){
24222         html = String(html);
24223         if(html.length > 5){
24224             if(Roo.isSafari){ // strip safari nonsense
24225                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24226             }
24227         }
24228         if(html == '&nbsp;'){
24229             html = '';
24230         }
24231         return html;
24232     },
24233
24234     /**
24235      * HTML Editor -> Textarea
24236      * Protected method that will not generally be called directly. Syncs the contents
24237      * of the editor iframe with the textarea.
24238      */
24239     syncValue : function(){
24240         if(this.initialized){
24241             var bd = (this.doc.body || this.doc.documentElement);
24242             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24243             var html = bd.innerHTML;
24244             if(Roo.isSafari){
24245                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24246                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24247                 if(m && m[1]){
24248                     html = '<div style="'+m[0]+'">' + html + '</div>';
24249                 }
24250             }
24251             html = this.cleanHtml(html);
24252             // fix up the special chars.. normaly like back quotes in word...
24253             // however we do not want to do this with chinese..
24254             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24255                 
24256                 var cc = match.charCodeAt();
24257
24258                 // Get the character value, handling surrogate pairs
24259                 if (match.length == 2) {
24260                     // It's a surrogate pair, calculate the Unicode code point
24261                     var high = match.charCodeAt(0) - 0xD800;
24262                     var low  = match.charCodeAt(1) - 0xDC00;
24263                     cc = (high * 0x400) + low + 0x10000;
24264                 }  else if (
24265                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24266                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24267                     (cc >= 0xf900 && cc < 0xfb00 )
24268                 ) {
24269                         return match;
24270                 }  
24271          
24272                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24273                 return "&#" + cc + ";";
24274                 
24275                 
24276             });
24277             
24278             
24279              
24280             if(this.owner.fireEvent('beforesync', this, html) !== false){
24281                 this.el.dom.value = html;
24282                 this.owner.fireEvent('sync', this, html);
24283             }
24284         }
24285     },
24286
24287     /**
24288      * Protected method that will not generally be called directly. Pushes the value of the textarea
24289      * into the iframe editor.
24290      */
24291     pushValue : function(){
24292         if(this.initialized){
24293             var v = this.el.dom.value.trim();
24294             
24295 //            if(v.length < 1){
24296 //                v = '&#160;';
24297 //            }
24298             
24299             if(this.owner.fireEvent('beforepush', this, v) !== false){
24300                 var d = (this.doc.body || this.doc.documentElement);
24301                 d.innerHTML = v;
24302                 this.cleanUpPaste();
24303                 this.el.dom.value = d.innerHTML;
24304                 this.owner.fireEvent('push', this, v);
24305             }
24306         }
24307     },
24308
24309     // private
24310     deferFocus : function(){
24311         this.focus.defer(10, this);
24312     },
24313
24314     // doc'ed in Field
24315     focus : function(){
24316         if(this.win && !this.sourceEditMode){
24317             this.win.focus();
24318         }else{
24319             this.el.focus();
24320         }
24321     },
24322     
24323     assignDocWin: function()
24324     {
24325         var iframe = this.iframe;
24326         
24327          if(Roo.isIE){
24328             this.doc = iframe.contentWindow.document;
24329             this.win = iframe.contentWindow;
24330         } else {
24331 //            if (!Roo.get(this.frameId)) {
24332 //                return;
24333 //            }
24334 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24335 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24336             
24337             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24338                 return;
24339             }
24340             
24341             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24342             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24343         }
24344     },
24345     
24346     // private
24347     initEditor : function(){
24348         //console.log("INIT EDITOR");
24349         this.assignDocWin();
24350         
24351         
24352         
24353         this.doc.designMode="on";
24354         this.doc.open();
24355         this.doc.write(this.getDocMarkup());
24356         this.doc.close();
24357         
24358         var dbody = (this.doc.body || this.doc.documentElement);
24359         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24360         // this copies styles from the containing element into thsi one..
24361         // not sure why we need all of this..
24362         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24363         
24364         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24365         //ss['background-attachment'] = 'fixed'; // w3c
24366         dbody.bgProperties = 'fixed'; // ie
24367         //Roo.DomHelper.applyStyles(dbody, ss);
24368         Roo.EventManager.on(this.doc, {
24369             //'mousedown': this.onEditorEvent,
24370             'mouseup': this.onEditorEvent,
24371             'dblclick': this.onEditorEvent,
24372             'click': this.onEditorEvent,
24373             'keyup': this.onEditorEvent,
24374             buffer:100,
24375             scope: this
24376         });
24377         if(Roo.isGecko){
24378             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24379         }
24380         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24381             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24382         }
24383         this.initialized = true;
24384
24385         this.owner.fireEvent('initialize', this);
24386         this.pushValue();
24387     },
24388
24389     // private
24390     onDestroy : function(){
24391         
24392         
24393         
24394         if(this.rendered){
24395             
24396             //for (var i =0; i < this.toolbars.length;i++) {
24397             //    // fixme - ask toolbars for heights?
24398             //    this.toolbars[i].onDestroy();
24399            // }
24400             
24401             //this.wrap.dom.innerHTML = '';
24402             //this.wrap.remove();
24403         }
24404     },
24405
24406     // private
24407     onFirstFocus : function(){
24408         
24409         this.assignDocWin();
24410         
24411         
24412         this.activated = true;
24413          
24414     
24415         if(Roo.isGecko){ // prevent silly gecko errors
24416             this.win.focus();
24417             var s = this.win.getSelection();
24418             if(!s.focusNode || s.focusNode.nodeType != 3){
24419                 var r = s.getRangeAt(0);
24420                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24421                 r.collapse(true);
24422                 this.deferFocus();
24423             }
24424             try{
24425                 this.execCmd('useCSS', true);
24426                 this.execCmd('styleWithCSS', false);
24427             }catch(e){}
24428         }
24429         this.owner.fireEvent('activate', this);
24430     },
24431
24432     // private
24433     adjustFont: function(btn){
24434         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24435         //if(Roo.isSafari){ // safari
24436         //    adjust *= 2;
24437        // }
24438         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24439         if(Roo.isSafari){ // safari
24440             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24441             v =  (v < 10) ? 10 : v;
24442             v =  (v > 48) ? 48 : v;
24443             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24444             
24445         }
24446         
24447         
24448         v = Math.max(1, v+adjust);
24449         
24450         this.execCmd('FontSize', v  );
24451     },
24452
24453     onEditorEvent : function(e)
24454     {
24455         this.owner.fireEvent('editorevent', this, e);
24456       //  this.updateToolbar();
24457         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24458     },
24459
24460     insertTag : function(tg)
24461     {
24462         // could be a bit smarter... -> wrap the current selected tRoo..
24463         if (tg.toLowerCase() == 'span' ||
24464             tg.toLowerCase() == 'code' ||
24465             tg.toLowerCase() == 'sup' ||
24466             tg.toLowerCase() == 'sub' 
24467             ) {
24468             
24469             range = this.createRange(this.getSelection());
24470             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24471             wrappingNode.appendChild(range.extractContents());
24472             range.insertNode(wrappingNode);
24473
24474             return;
24475             
24476             
24477             
24478         }
24479         this.execCmd("formatblock",   tg);
24480         
24481     },
24482     
24483     insertText : function(txt)
24484     {
24485         
24486         
24487         var range = this.createRange();
24488         range.deleteContents();
24489                //alert(Sender.getAttribute('label'));
24490                
24491         range.insertNode(this.doc.createTextNode(txt));
24492     } ,
24493     
24494      
24495
24496     /**
24497      * Executes a Midas editor command on the editor document and performs necessary focus and
24498      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24499      * @param {String} cmd The Midas command
24500      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24501      */
24502     relayCmd : function(cmd, value){
24503         this.win.focus();
24504         this.execCmd(cmd, value);
24505         this.owner.fireEvent('editorevent', this);
24506         //this.updateToolbar();
24507         this.owner.deferFocus();
24508     },
24509
24510     /**
24511      * Executes a Midas editor command directly on the editor document.
24512      * For visual commands, you should use {@link #relayCmd} instead.
24513      * <b>This should only be called after the editor is initialized.</b>
24514      * @param {String} cmd The Midas command
24515      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24516      */
24517     execCmd : function(cmd, value){
24518         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24519         this.syncValue();
24520     },
24521  
24522  
24523    
24524     /**
24525      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24526      * to insert tRoo.
24527      * @param {String} text | dom node.. 
24528      */
24529     insertAtCursor : function(text)
24530     {
24531         
24532         if(!this.activated){
24533             return;
24534         }
24535         /*
24536         if(Roo.isIE){
24537             this.win.focus();
24538             var r = this.doc.selection.createRange();
24539             if(r){
24540                 r.collapse(true);
24541                 r.pasteHTML(text);
24542                 this.syncValue();
24543                 this.deferFocus();
24544             
24545             }
24546             return;
24547         }
24548         */
24549         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24550             this.win.focus();
24551             
24552             
24553             // from jquery ui (MIT licenced)
24554             var range, node;
24555             var win = this.win;
24556             
24557             if (win.getSelection && win.getSelection().getRangeAt) {
24558                 range = win.getSelection().getRangeAt(0);
24559                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24560                 range.insertNode(node);
24561             } else if (win.document.selection && win.document.selection.createRange) {
24562                 // no firefox support
24563                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24564                 win.document.selection.createRange().pasteHTML(txt);
24565             } else {
24566                 // no firefox support
24567                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24568                 this.execCmd('InsertHTML', txt);
24569             } 
24570             
24571             this.syncValue();
24572             
24573             this.deferFocus();
24574         }
24575     },
24576  // private
24577     mozKeyPress : function(e){
24578         if(e.ctrlKey){
24579             var c = e.getCharCode(), cmd;
24580           
24581             if(c > 0){
24582                 c = String.fromCharCode(c).toLowerCase();
24583                 switch(c){
24584                     case 'b':
24585                         cmd = 'bold';
24586                         break;
24587                     case 'i':
24588                         cmd = 'italic';
24589                         break;
24590                     
24591                     case 'u':
24592                         cmd = 'underline';
24593                         break;
24594                     
24595                     case 'v':
24596                         this.cleanUpPaste.defer(100, this);
24597                         return;
24598                         
24599                 }
24600                 if(cmd){
24601                     this.win.focus();
24602                     this.execCmd(cmd);
24603                     this.deferFocus();
24604                     e.preventDefault();
24605                 }
24606                 
24607             }
24608         }
24609     },
24610
24611     // private
24612     fixKeys : function(){ // load time branching for fastest keydown performance
24613         if(Roo.isIE){
24614             return function(e){
24615                 var k = e.getKey(), r;
24616                 if(k == e.TAB){
24617                     e.stopEvent();
24618                     r = this.doc.selection.createRange();
24619                     if(r){
24620                         r.collapse(true);
24621                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24622                         this.deferFocus();
24623                     }
24624                     return;
24625                 }
24626                 
24627                 if(k == e.ENTER){
24628                     r = this.doc.selection.createRange();
24629                     if(r){
24630                         var target = r.parentElement();
24631                         if(!target || target.tagName.toLowerCase() != 'li'){
24632                             e.stopEvent();
24633                             r.pasteHTML('<br />');
24634                             r.collapse(false);
24635                             r.select();
24636                         }
24637                     }
24638                 }
24639                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24640                     this.cleanUpPaste.defer(100, this);
24641                     return;
24642                 }
24643                 
24644                 
24645             };
24646         }else if(Roo.isOpera){
24647             return function(e){
24648                 var k = e.getKey();
24649                 if(k == e.TAB){
24650                     e.stopEvent();
24651                     this.win.focus();
24652                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24653                     this.deferFocus();
24654                 }
24655                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24656                     this.cleanUpPaste.defer(100, this);
24657                     return;
24658                 }
24659                 
24660             };
24661         }else if(Roo.isSafari){
24662             return function(e){
24663                 var k = e.getKey();
24664                 
24665                 if(k == e.TAB){
24666                     e.stopEvent();
24667                     this.execCmd('InsertText','\t');
24668                     this.deferFocus();
24669                     return;
24670                 }
24671                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24672                     this.cleanUpPaste.defer(100, this);
24673                     return;
24674                 }
24675                 
24676              };
24677         }
24678     }(),
24679     
24680     getAllAncestors: function()
24681     {
24682         var p = this.getSelectedNode();
24683         var a = [];
24684         if (!p) {
24685             a.push(p); // push blank onto stack..
24686             p = this.getParentElement();
24687         }
24688         
24689         
24690         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24691             a.push(p);
24692             p = p.parentNode;
24693         }
24694         a.push(this.doc.body);
24695         return a;
24696     },
24697     lastSel : false,
24698     lastSelNode : false,
24699     
24700     
24701     getSelection : function() 
24702     {
24703         this.assignDocWin();
24704         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24705     },
24706     
24707     getSelectedNode: function() 
24708     {
24709         // this may only work on Gecko!!!
24710         
24711         // should we cache this!!!!
24712         
24713         
24714         
24715          
24716         var range = this.createRange(this.getSelection()).cloneRange();
24717         
24718         if (Roo.isIE) {
24719             var parent = range.parentElement();
24720             while (true) {
24721                 var testRange = range.duplicate();
24722                 testRange.moveToElementText(parent);
24723                 if (testRange.inRange(range)) {
24724                     break;
24725                 }
24726                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24727                     break;
24728                 }
24729                 parent = parent.parentElement;
24730             }
24731             return parent;
24732         }
24733         
24734         // is ancestor a text element.
24735         var ac =  range.commonAncestorContainer;
24736         if (ac.nodeType == 3) {
24737             ac = ac.parentNode;
24738         }
24739         
24740         var ar = ac.childNodes;
24741          
24742         var nodes = [];
24743         var other_nodes = [];
24744         var has_other_nodes = false;
24745         for (var i=0;i<ar.length;i++) {
24746             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24747                 continue;
24748             }
24749             // fullly contained node.
24750             
24751             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24752                 nodes.push(ar[i]);
24753                 continue;
24754             }
24755             
24756             // probably selected..
24757             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24758                 other_nodes.push(ar[i]);
24759                 continue;
24760             }
24761             // outer..
24762             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24763                 continue;
24764             }
24765             
24766             
24767             has_other_nodes = true;
24768         }
24769         if (!nodes.length && other_nodes.length) {
24770             nodes= other_nodes;
24771         }
24772         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24773             return false;
24774         }
24775         
24776         return nodes[0];
24777     },
24778     createRange: function(sel)
24779     {
24780         // this has strange effects when using with 
24781         // top toolbar - not sure if it's a great idea.
24782         //this.editor.contentWindow.focus();
24783         if (typeof sel != "undefined") {
24784             try {
24785                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24786             } catch(e) {
24787                 return this.doc.createRange();
24788             }
24789         } else {
24790             return this.doc.createRange();
24791         }
24792     },
24793     getParentElement: function()
24794     {
24795         
24796         this.assignDocWin();
24797         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24798         
24799         var range = this.createRange(sel);
24800          
24801         try {
24802             var p = range.commonAncestorContainer;
24803             while (p.nodeType == 3) { // text node
24804                 p = p.parentNode;
24805             }
24806             return p;
24807         } catch (e) {
24808             return null;
24809         }
24810     
24811     },
24812     /***
24813      *
24814      * Range intersection.. the hard stuff...
24815      *  '-1' = before
24816      *  '0' = hits..
24817      *  '1' = after.
24818      *         [ -- selected range --- ]
24819      *   [fail]                        [fail]
24820      *
24821      *    basically..
24822      *      if end is before start or  hits it. fail.
24823      *      if start is after end or hits it fail.
24824      *
24825      *   if either hits (but other is outside. - then it's not 
24826      *   
24827      *    
24828      **/
24829     
24830     
24831     // @see http://www.thismuchiknow.co.uk/?p=64.
24832     rangeIntersectsNode : function(range, node)
24833     {
24834         var nodeRange = node.ownerDocument.createRange();
24835         try {
24836             nodeRange.selectNode(node);
24837         } catch (e) {
24838             nodeRange.selectNodeContents(node);
24839         }
24840     
24841         var rangeStartRange = range.cloneRange();
24842         rangeStartRange.collapse(true);
24843     
24844         var rangeEndRange = range.cloneRange();
24845         rangeEndRange.collapse(false);
24846     
24847         var nodeStartRange = nodeRange.cloneRange();
24848         nodeStartRange.collapse(true);
24849     
24850         var nodeEndRange = nodeRange.cloneRange();
24851         nodeEndRange.collapse(false);
24852     
24853         return rangeStartRange.compareBoundaryPoints(
24854                  Range.START_TO_START, nodeEndRange) == -1 &&
24855                rangeEndRange.compareBoundaryPoints(
24856                  Range.START_TO_START, nodeStartRange) == 1;
24857         
24858          
24859     },
24860     rangeCompareNode : function(range, node)
24861     {
24862         var nodeRange = node.ownerDocument.createRange();
24863         try {
24864             nodeRange.selectNode(node);
24865         } catch (e) {
24866             nodeRange.selectNodeContents(node);
24867         }
24868         
24869         
24870         range.collapse(true);
24871     
24872         nodeRange.collapse(true);
24873      
24874         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24875         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24876          
24877         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24878         
24879         var nodeIsBefore   =  ss == 1;
24880         var nodeIsAfter    = ee == -1;
24881         
24882         if (nodeIsBefore && nodeIsAfter) {
24883             return 0; // outer
24884         }
24885         if (!nodeIsBefore && nodeIsAfter) {
24886             return 1; //right trailed.
24887         }
24888         
24889         if (nodeIsBefore && !nodeIsAfter) {
24890             return 2;  // left trailed.
24891         }
24892         // fully contined.
24893         return 3;
24894     },
24895
24896     // private? - in a new class?
24897     cleanUpPaste :  function()
24898     {
24899         // cleans up the whole document..
24900         Roo.log('cleanuppaste');
24901         
24902         this.cleanUpChildren(this.doc.body);
24903         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24904         if (clean != this.doc.body.innerHTML) {
24905             this.doc.body.innerHTML = clean;
24906         }
24907         
24908     },
24909     
24910     cleanWordChars : function(input) {// change the chars to hex code
24911         var he = Roo.HtmlEditorCore;
24912         
24913         var output = input;
24914         Roo.each(he.swapCodes, function(sw) { 
24915             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24916             
24917             output = output.replace(swapper, sw[1]);
24918         });
24919         
24920         return output;
24921     },
24922     
24923     
24924     cleanUpChildren : function (n)
24925     {
24926         if (!n.childNodes.length) {
24927             return;
24928         }
24929         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24930            this.cleanUpChild(n.childNodes[i]);
24931         }
24932     },
24933     
24934     
24935         
24936     
24937     cleanUpChild : function (node)
24938     {
24939         var ed = this;
24940         //console.log(node);
24941         if (node.nodeName == "#text") {
24942             // clean up silly Windows -- stuff?
24943             return; 
24944         }
24945         if (node.nodeName == "#comment") {
24946             node.parentNode.removeChild(node);
24947             // clean up silly Windows -- stuff?
24948             return; 
24949         }
24950         var lcname = node.tagName.toLowerCase();
24951         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24952         // whitelist of tags..
24953         
24954         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24955             // remove node.
24956             node.parentNode.removeChild(node);
24957             return;
24958             
24959         }
24960         
24961         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24962         
24963         // spans with no attributes - just remove them..
24964         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24965             remove_keep_children = true;
24966         }
24967         
24968         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24969         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24970         
24971         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24972         //    remove_keep_children = true;
24973         //}
24974         
24975         if (remove_keep_children) {
24976             this.cleanUpChildren(node);
24977             // inserts everything just before this node...
24978             while (node.childNodes.length) {
24979                 var cn = node.childNodes[0];
24980                 node.removeChild(cn);
24981                 node.parentNode.insertBefore(cn, node);
24982             }
24983             node.parentNode.removeChild(node);
24984             return;
24985         }
24986         
24987         if (!node.attributes || !node.attributes.length) {
24988             
24989           
24990             
24991             
24992             this.cleanUpChildren(node);
24993             return;
24994         }
24995         
24996         function cleanAttr(n,v)
24997         {
24998             
24999             if (v.match(/^\./) || v.match(/^\//)) {
25000                 return;
25001             }
25002             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25003                 return;
25004             }
25005             if (v.match(/^#/)) {
25006                 return;
25007             }
25008             if (v.match(/^\{/)) { // allow template editing.
25009                 return;
25010             }
25011 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25012             node.removeAttribute(n);
25013             
25014         }
25015         
25016         var cwhite = this.cwhite;
25017         var cblack = this.cblack;
25018             
25019         function cleanStyle(n,v)
25020         {
25021             if (v.match(/expression/)) { //XSS?? should we even bother..
25022                 node.removeAttribute(n);
25023                 return;
25024             }
25025             
25026             var parts = v.split(/;/);
25027             var clean = [];
25028             
25029             Roo.each(parts, function(p) {
25030                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25031                 if (!p.length) {
25032                     return true;
25033                 }
25034                 var l = p.split(':').shift().replace(/\s+/g,'');
25035                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25036                 
25037                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25038 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25039                     //node.removeAttribute(n);
25040                     return true;
25041                 }
25042                 //Roo.log()
25043                 // only allow 'c whitelisted system attributes'
25044                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25045 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25046                     //node.removeAttribute(n);
25047                     return true;
25048                 }
25049                 
25050                 
25051                  
25052                 
25053                 clean.push(p);
25054                 return true;
25055             });
25056             if (clean.length) { 
25057                 node.setAttribute(n, clean.join(';'));
25058             } else {
25059                 node.removeAttribute(n);
25060             }
25061             
25062         }
25063         
25064         
25065         for (var i = node.attributes.length-1; i > -1 ; i--) {
25066             var a = node.attributes[i];
25067             //console.log(a);
25068             
25069             if (a.name.toLowerCase().substr(0,2)=='on')  {
25070                 node.removeAttribute(a.name);
25071                 continue;
25072             }
25073             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25074                 node.removeAttribute(a.name);
25075                 continue;
25076             }
25077             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25078                 cleanAttr(a.name,a.value); // fixme..
25079                 continue;
25080             }
25081             if (a.name == 'style') {
25082                 cleanStyle(a.name,a.value);
25083                 continue;
25084             }
25085             /// clean up MS crap..
25086             // tecnically this should be a list of valid class'es..
25087             
25088             
25089             if (a.name == 'class') {
25090                 if (a.value.match(/^Mso/)) {
25091                     node.removeAttribute('class');
25092                 }
25093                 
25094                 if (a.value.match(/^body$/)) {
25095                     node.removeAttribute('class');
25096                 }
25097                 continue;
25098             }
25099             
25100             // style cleanup!?
25101             // class cleanup?
25102             
25103         }
25104         
25105         
25106         this.cleanUpChildren(node);
25107         
25108         
25109     },
25110     
25111     /**
25112      * Clean up MS wordisms...
25113      */
25114     cleanWord : function(node)
25115     {
25116         if (!node) {
25117             this.cleanWord(this.doc.body);
25118             return;
25119         }
25120         
25121         if(
25122                 node.nodeName == 'SPAN' &&
25123                 !node.hasAttributes() &&
25124                 node.childNodes.length == 1 &&
25125                 node.firstChild.nodeName == "#text"  
25126         ) {
25127             var textNode = node.firstChild;
25128             node.removeChild(textNode);
25129             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25130                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25131             }
25132             node.parentNode.insertBefore(textNode, node);
25133             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25134                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25135             }
25136             node.parentNode.removeChild(node);
25137         }
25138         
25139         if (node.nodeName == "#text") {
25140             // clean up silly Windows -- stuff?
25141             return; 
25142         }
25143         if (node.nodeName == "#comment") {
25144             node.parentNode.removeChild(node);
25145             // clean up silly Windows -- stuff?
25146             return; 
25147         }
25148         
25149         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25150             node.parentNode.removeChild(node);
25151             return;
25152         }
25153         //Roo.log(node.tagName);
25154         // remove - but keep children..
25155         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25156             //Roo.log('-- removed');
25157             while (node.childNodes.length) {
25158                 var cn = node.childNodes[0];
25159                 node.removeChild(cn);
25160                 node.parentNode.insertBefore(cn, node);
25161                 // move node to parent - and clean it..
25162                 this.cleanWord(cn);
25163             }
25164             node.parentNode.removeChild(node);
25165             /// no need to iterate chidlren = it's got none..
25166             //this.iterateChildren(node, this.cleanWord);
25167             return;
25168         }
25169         // clean styles
25170         if (node.className.length) {
25171             
25172             var cn = node.className.split(/\W+/);
25173             var cna = [];
25174             Roo.each(cn, function(cls) {
25175                 if (cls.match(/Mso[a-zA-Z]+/)) {
25176                     return;
25177                 }
25178                 cna.push(cls);
25179             });
25180             node.className = cna.length ? cna.join(' ') : '';
25181             if (!cna.length) {
25182                 node.removeAttribute("class");
25183             }
25184         }
25185         
25186         if (node.hasAttribute("lang")) {
25187             node.removeAttribute("lang");
25188         }
25189         
25190         if (node.hasAttribute("style")) {
25191             
25192             var styles = node.getAttribute("style").split(";");
25193             var nstyle = [];
25194             Roo.each(styles, function(s) {
25195                 if (!s.match(/:/)) {
25196                     return;
25197                 }
25198                 var kv = s.split(":");
25199                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25200                     return;
25201                 }
25202                 // what ever is left... we allow.
25203                 nstyle.push(s);
25204             });
25205             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25206             if (!nstyle.length) {
25207                 node.removeAttribute('style');
25208             }
25209         }
25210         this.iterateChildren(node, this.cleanWord);
25211         
25212         
25213         
25214     },
25215     /**
25216      * iterateChildren of a Node, calling fn each time, using this as the scole..
25217      * @param {DomNode} node node to iterate children of.
25218      * @param {Function} fn method of this class to call on each item.
25219      */
25220     iterateChildren : function(node, fn)
25221     {
25222         if (!node.childNodes.length) {
25223                 return;
25224         }
25225         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25226            fn.call(this, node.childNodes[i])
25227         }
25228     },
25229     
25230     
25231     /**
25232      * cleanTableWidths.
25233      *
25234      * Quite often pasting from word etc.. results in tables with column and widths.
25235      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25236      *
25237      */
25238     cleanTableWidths : function(node)
25239     {
25240          
25241          
25242         if (!node) {
25243             this.cleanTableWidths(this.doc.body);
25244             return;
25245         }
25246         
25247         // ignore list...
25248         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25249             return; 
25250         }
25251         Roo.log(node.tagName);
25252         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25253             this.iterateChildren(node, this.cleanTableWidths);
25254             return;
25255         }
25256         if (node.hasAttribute('width')) {
25257             node.removeAttribute('width');
25258         }
25259         
25260          
25261         if (node.hasAttribute("style")) {
25262             // pretty basic...
25263             
25264             var styles = node.getAttribute("style").split(";");
25265             var nstyle = [];
25266             Roo.each(styles, function(s) {
25267                 if (!s.match(/:/)) {
25268                     return;
25269                 }
25270                 var kv = s.split(":");
25271                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25272                     return;
25273                 }
25274                 // what ever is left... we allow.
25275                 nstyle.push(s);
25276             });
25277             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25278             if (!nstyle.length) {
25279                 node.removeAttribute('style');
25280             }
25281         }
25282         
25283         this.iterateChildren(node, this.cleanTableWidths);
25284         
25285         
25286     },
25287     
25288     
25289     
25290     
25291     domToHTML : function(currentElement, depth, nopadtext) {
25292         
25293         depth = depth || 0;
25294         nopadtext = nopadtext || false;
25295     
25296         if (!currentElement) {
25297             return this.domToHTML(this.doc.body);
25298         }
25299         
25300         //Roo.log(currentElement);
25301         var j;
25302         var allText = false;
25303         var nodeName = currentElement.nodeName;
25304         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25305         
25306         if  (nodeName == '#text') {
25307             
25308             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25309         }
25310         
25311         
25312         var ret = '';
25313         if (nodeName != 'BODY') {
25314              
25315             var i = 0;
25316             // Prints the node tagName, such as <A>, <IMG>, etc
25317             if (tagName) {
25318                 var attr = [];
25319                 for(i = 0; i < currentElement.attributes.length;i++) {
25320                     // quoting?
25321                     var aname = currentElement.attributes.item(i).name;
25322                     if (!currentElement.attributes.item(i).value.length) {
25323                         continue;
25324                     }
25325                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25326                 }
25327                 
25328                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25329             } 
25330             else {
25331                 
25332                 // eack
25333             }
25334         } else {
25335             tagName = false;
25336         }
25337         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25338             return ret;
25339         }
25340         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25341             nopadtext = true;
25342         }
25343         
25344         
25345         // Traverse the tree
25346         i = 0;
25347         var currentElementChild = currentElement.childNodes.item(i);
25348         var allText = true;
25349         var innerHTML  = '';
25350         lastnode = '';
25351         while (currentElementChild) {
25352             // Formatting code (indent the tree so it looks nice on the screen)
25353             var nopad = nopadtext;
25354             if (lastnode == 'SPAN') {
25355                 nopad  = true;
25356             }
25357             // text
25358             if  (currentElementChild.nodeName == '#text') {
25359                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25360                 toadd = nopadtext ? toadd : toadd.trim();
25361                 if (!nopad && toadd.length > 80) {
25362                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25363                 }
25364                 innerHTML  += toadd;
25365                 
25366                 i++;
25367                 currentElementChild = currentElement.childNodes.item(i);
25368                 lastNode = '';
25369                 continue;
25370             }
25371             allText = false;
25372             
25373             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25374                 
25375             // Recursively traverse the tree structure of the child node
25376             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25377             lastnode = currentElementChild.nodeName;
25378             i++;
25379             currentElementChild=currentElement.childNodes.item(i);
25380         }
25381         
25382         ret += innerHTML;
25383         
25384         if (!allText) {
25385                 // The remaining code is mostly for formatting the tree
25386             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25387         }
25388         
25389         
25390         if (tagName) {
25391             ret+= "</"+tagName+">";
25392         }
25393         return ret;
25394         
25395     },
25396         
25397     applyBlacklists : function()
25398     {
25399         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25400         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25401         
25402         this.white = [];
25403         this.black = [];
25404         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25405             if (b.indexOf(tag) > -1) {
25406                 return;
25407             }
25408             this.white.push(tag);
25409             
25410         }, this);
25411         
25412         Roo.each(w, function(tag) {
25413             if (b.indexOf(tag) > -1) {
25414                 return;
25415             }
25416             if (this.white.indexOf(tag) > -1) {
25417                 return;
25418             }
25419             this.white.push(tag);
25420             
25421         }, this);
25422         
25423         
25424         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25425             if (w.indexOf(tag) > -1) {
25426                 return;
25427             }
25428             this.black.push(tag);
25429             
25430         }, this);
25431         
25432         Roo.each(b, function(tag) {
25433             if (w.indexOf(tag) > -1) {
25434                 return;
25435             }
25436             if (this.black.indexOf(tag) > -1) {
25437                 return;
25438             }
25439             this.black.push(tag);
25440             
25441         }, this);
25442         
25443         
25444         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25445         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25446         
25447         this.cwhite = [];
25448         this.cblack = [];
25449         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25450             if (b.indexOf(tag) > -1) {
25451                 return;
25452             }
25453             this.cwhite.push(tag);
25454             
25455         }, this);
25456         
25457         Roo.each(w, function(tag) {
25458             if (b.indexOf(tag) > -1) {
25459                 return;
25460             }
25461             if (this.cwhite.indexOf(tag) > -1) {
25462                 return;
25463             }
25464             this.cwhite.push(tag);
25465             
25466         }, this);
25467         
25468         
25469         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25470             if (w.indexOf(tag) > -1) {
25471                 return;
25472             }
25473             this.cblack.push(tag);
25474             
25475         }, this);
25476         
25477         Roo.each(b, function(tag) {
25478             if (w.indexOf(tag) > -1) {
25479                 return;
25480             }
25481             if (this.cblack.indexOf(tag) > -1) {
25482                 return;
25483             }
25484             this.cblack.push(tag);
25485             
25486         }, this);
25487     },
25488     
25489     setStylesheets : function(stylesheets)
25490     {
25491         if(typeof(stylesheets) == 'string'){
25492             Roo.get(this.iframe.contentDocument.head).createChild({
25493                 tag : 'link',
25494                 rel : 'stylesheet',
25495                 type : 'text/css',
25496                 href : stylesheets
25497             });
25498             
25499             return;
25500         }
25501         var _this = this;
25502      
25503         Roo.each(stylesheets, function(s) {
25504             if(!s.length){
25505                 return;
25506             }
25507             
25508             Roo.get(_this.iframe.contentDocument.head).createChild({
25509                 tag : 'link',
25510                 rel : 'stylesheet',
25511                 type : 'text/css',
25512                 href : s
25513             });
25514         });
25515
25516         
25517     },
25518     
25519     removeStylesheets : function()
25520     {
25521         var _this = this;
25522         
25523         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25524             s.remove();
25525         });
25526     },
25527     
25528     setStyle : function(style)
25529     {
25530         Roo.get(this.iframe.contentDocument.head).createChild({
25531             tag : 'style',
25532             type : 'text/css',
25533             html : style
25534         });
25535
25536         return;
25537     }
25538     
25539     // hide stuff that is not compatible
25540     /**
25541      * @event blur
25542      * @hide
25543      */
25544     /**
25545      * @event change
25546      * @hide
25547      */
25548     /**
25549      * @event focus
25550      * @hide
25551      */
25552     /**
25553      * @event specialkey
25554      * @hide
25555      */
25556     /**
25557      * @cfg {String} fieldClass @hide
25558      */
25559     /**
25560      * @cfg {String} focusClass @hide
25561      */
25562     /**
25563      * @cfg {String} autoCreate @hide
25564      */
25565     /**
25566      * @cfg {String} inputType @hide
25567      */
25568     /**
25569      * @cfg {String} invalidClass @hide
25570      */
25571     /**
25572      * @cfg {String} invalidText @hide
25573      */
25574     /**
25575      * @cfg {String} msgFx @hide
25576      */
25577     /**
25578      * @cfg {String} validateOnBlur @hide
25579      */
25580 });
25581
25582 Roo.HtmlEditorCore.white = [
25583         'area', 'br', 'img', 'input', 'hr', 'wbr',
25584         
25585        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25586        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25587        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25588        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25589        'table',   'ul',         'xmp', 
25590        
25591        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25592       'thead',   'tr', 
25593      
25594       'dir', 'menu', 'ol', 'ul', 'dl',
25595        
25596       'embed',  'object'
25597 ];
25598
25599
25600 Roo.HtmlEditorCore.black = [
25601     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25602         'applet', // 
25603         'base',   'basefont', 'bgsound', 'blink',  'body', 
25604         'frame',  'frameset', 'head',    'html',   'ilayer', 
25605         'iframe', 'layer',  'link',     'meta',    'object',   
25606         'script', 'style' ,'title',  'xml' // clean later..
25607 ];
25608 Roo.HtmlEditorCore.clean = [
25609     'script', 'style', 'title', 'xml'
25610 ];
25611 Roo.HtmlEditorCore.remove = [
25612     'font'
25613 ];
25614 // attributes..
25615
25616 Roo.HtmlEditorCore.ablack = [
25617     'on'
25618 ];
25619     
25620 Roo.HtmlEditorCore.aclean = [ 
25621     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25622 ];
25623
25624 // protocols..
25625 Roo.HtmlEditorCore.pwhite= [
25626         'http',  'https',  'mailto'
25627 ];
25628
25629 // white listed style attributes.
25630 Roo.HtmlEditorCore.cwhite= [
25631       //  'text-align', /// default is to allow most things..
25632       
25633          
25634 //        'font-size'//??
25635 ];
25636
25637 // black listed style attributes.
25638 Roo.HtmlEditorCore.cblack= [
25639       //  'font-size' -- this can be set by the project 
25640 ];
25641
25642
25643 Roo.HtmlEditorCore.swapCodes   =[ 
25644     [    8211, "--" ], 
25645     [    8212, "--" ], 
25646     [    8216,  "'" ],  
25647     [    8217, "'" ],  
25648     [    8220, '"' ],  
25649     [    8221, '"' ],  
25650     [    8226, "*" ],  
25651     [    8230, "..." ]
25652 ]; 
25653
25654     /*
25655  * - LGPL
25656  *
25657  * HtmlEditor
25658  * 
25659  */
25660
25661 /**
25662  * @class Roo.bootstrap.HtmlEditor
25663  * @extends Roo.bootstrap.TextArea
25664  * Bootstrap HtmlEditor class
25665
25666  * @constructor
25667  * Create a new HtmlEditor
25668  * @param {Object} config The config object
25669  */
25670
25671 Roo.bootstrap.HtmlEditor = function(config){
25672     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25673     if (!this.toolbars) {
25674         this.toolbars = [];
25675     }
25676     
25677     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25678     this.addEvents({
25679             /**
25680              * @event initialize
25681              * Fires when the editor is fully initialized (including the iframe)
25682              * @param {HtmlEditor} this
25683              */
25684             initialize: true,
25685             /**
25686              * @event activate
25687              * Fires when the editor is first receives the focus. Any insertion must wait
25688              * until after this event.
25689              * @param {HtmlEditor} this
25690              */
25691             activate: true,
25692              /**
25693              * @event beforesync
25694              * Fires before the textarea is updated with content from the editor iframe. Return false
25695              * to cancel the sync.
25696              * @param {HtmlEditor} this
25697              * @param {String} html
25698              */
25699             beforesync: true,
25700              /**
25701              * @event beforepush
25702              * Fires before the iframe editor is updated with content from the textarea. Return false
25703              * to cancel the push.
25704              * @param {HtmlEditor} this
25705              * @param {String} html
25706              */
25707             beforepush: true,
25708              /**
25709              * @event sync
25710              * Fires when the textarea is updated with content from the editor iframe.
25711              * @param {HtmlEditor} this
25712              * @param {String} html
25713              */
25714             sync: true,
25715              /**
25716              * @event push
25717              * Fires when the iframe editor is updated with content from the textarea.
25718              * @param {HtmlEditor} this
25719              * @param {String} html
25720              */
25721             push: true,
25722              /**
25723              * @event editmodechange
25724              * Fires when the editor switches edit modes
25725              * @param {HtmlEditor} this
25726              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25727              */
25728             editmodechange: true,
25729             /**
25730              * @event editorevent
25731              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25732              * @param {HtmlEditor} this
25733              */
25734             editorevent: true,
25735             /**
25736              * @event firstfocus
25737              * Fires when on first focus - needed by toolbars..
25738              * @param {HtmlEditor} this
25739              */
25740             firstfocus: true,
25741             /**
25742              * @event autosave
25743              * Auto save the htmlEditor value as a file into Events
25744              * @param {HtmlEditor} this
25745              */
25746             autosave: true,
25747             /**
25748              * @event savedpreview
25749              * preview the saved version of htmlEditor
25750              * @param {HtmlEditor} this
25751              */
25752             savedpreview: true
25753         });
25754 };
25755
25756
25757 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25758     
25759     
25760       /**
25761      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25762      */
25763     toolbars : false,
25764     
25765      /**
25766     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25767     */
25768     btns : [],
25769    
25770      /**
25771      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25772      *                        Roo.resizable.
25773      */
25774     resizable : false,
25775      /**
25776      * @cfg {Number} height (in pixels)
25777      */   
25778     height: 300,
25779    /**
25780      * @cfg {Number} width (in pixels)
25781      */   
25782     width: false,
25783     
25784     /**
25785      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25786      * 
25787      */
25788     stylesheets: false,
25789     
25790     // id of frame..
25791     frameId: false,
25792     
25793     // private properties
25794     validationEvent : false,
25795     deferHeight: true,
25796     initialized : false,
25797     activated : false,
25798     
25799     onFocus : Roo.emptyFn,
25800     iframePad:3,
25801     hideMode:'offsets',
25802     
25803     tbContainer : false,
25804     
25805     bodyCls : '',
25806     
25807     toolbarContainer :function() {
25808         return this.wrap.select('.x-html-editor-tb',true).first();
25809     },
25810
25811     /**
25812      * Protected method that will not generally be called directly. It
25813      * is called when the editor creates its toolbar. Override this method if you need to
25814      * add custom toolbar buttons.
25815      * @param {HtmlEditor} editor
25816      */
25817     createToolbar : function(){
25818         Roo.log('renewing');
25819         Roo.log("create toolbars");
25820         
25821         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25822         this.toolbars[0].render(this.toolbarContainer());
25823         
25824         return;
25825         
25826 //        if (!editor.toolbars || !editor.toolbars.length) {
25827 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25828 //        }
25829 //        
25830 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25831 //            editor.toolbars[i] = Roo.factory(
25832 //                    typeof(editor.toolbars[i]) == 'string' ?
25833 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25834 //                Roo.bootstrap.HtmlEditor);
25835 //            editor.toolbars[i].init(editor);
25836 //        }
25837     },
25838
25839      
25840     // private
25841     onRender : function(ct, position)
25842     {
25843        // Roo.log("Call onRender: " + this.xtype);
25844         var _t = this;
25845         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25846       
25847         this.wrap = this.inputEl().wrap({
25848             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25849         });
25850         
25851         this.editorcore.onRender(ct, position);
25852          
25853         if (this.resizable) {
25854             this.resizeEl = new Roo.Resizable(this.wrap, {
25855                 pinned : true,
25856                 wrap: true,
25857                 dynamic : true,
25858                 minHeight : this.height,
25859                 height: this.height,
25860                 handles : this.resizable,
25861                 width: this.width,
25862                 listeners : {
25863                     resize : function(r, w, h) {
25864                         _t.onResize(w,h); // -something
25865                     }
25866                 }
25867             });
25868             
25869         }
25870         this.createToolbar(this);
25871        
25872         
25873         if(!this.width && this.resizable){
25874             this.setSize(this.wrap.getSize());
25875         }
25876         if (this.resizeEl) {
25877             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25878             // should trigger onReize..
25879         }
25880         
25881     },
25882
25883     // private
25884     onResize : function(w, h)
25885     {
25886         Roo.log('resize: ' +w + ',' + h );
25887         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25888         var ew = false;
25889         var eh = false;
25890         
25891         if(this.inputEl() ){
25892             if(typeof w == 'number'){
25893                 var aw = w - this.wrap.getFrameWidth('lr');
25894                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25895                 ew = aw;
25896             }
25897             if(typeof h == 'number'){
25898                  var tbh = -11;  // fixme it needs to tool bar size!
25899                 for (var i =0; i < this.toolbars.length;i++) {
25900                     // fixme - ask toolbars for heights?
25901                     tbh += this.toolbars[i].el.getHeight();
25902                     //if (this.toolbars[i].footer) {
25903                     //    tbh += this.toolbars[i].footer.el.getHeight();
25904                     //}
25905                 }
25906               
25907                 
25908                 
25909                 
25910                 
25911                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25912                 ah -= 5; // knock a few pixes off for look..
25913                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25914                 var eh = ah;
25915             }
25916         }
25917         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25918         this.editorcore.onResize(ew,eh);
25919         
25920     },
25921
25922     /**
25923      * Toggles the editor between standard and source edit mode.
25924      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25925      */
25926     toggleSourceEdit : function(sourceEditMode)
25927     {
25928         this.editorcore.toggleSourceEdit(sourceEditMode);
25929         
25930         if(this.editorcore.sourceEditMode){
25931             Roo.log('editor - showing textarea');
25932             
25933 //            Roo.log('in');
25934 //            Roo.log(this.syncValue());
25935             this.syncValue();
25936             this.inputEl().removeClass(['hide', 'x-hidden']);
25937             this.inputEl().dom.removeAttribute('tabIndex');
25938             this.inputEl().focus();
25939         }else{
25940             Roo.log('editor - hiding textarea');
25941 //            Roo.log('out')
25942 //            Roo.log(this.pushValue()); 
25943             this.pushValue();
25944             
25945             this.inputEl().addClass(['hide', 'x-hidden']);
25946             this.inputEl().dom.setAttribute('tabIndex', -1);
25947             //this.deferFocus();
25948         }
25949          
25950         if(this.resizable){
25951             this.setSize(this.wrap.getSize());
25952         }
25953         
25954         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25955     },
25956  
25957     // private (for BoxComponent)
25958     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25959
25960     // private (for BoxComponent)
25961     getResizeEl : function(){
25962         return this.wrap;
25963     },
25964
25965     // private (for BoxComponent)
25966     getPositionEl : function(){
25967         return this.wrap;
25968     },
25969
25970     // private
25971     initEvents : function(){
25972         this.originalValue = this.getValue();
25973     },
25974
25975 //    /**
25976 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25977 //     * @method
25978 //     */
25979 //    markInvalid : Roo.emptyFn,
25980 //    /**
25981 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25982 //     * @method
25983 //     */
25984 //    clearInvalid : Roo.emptyFn,
25985
25986     setValue : function(v){
25987         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25988         this.editorcore.pushValue();
25989     },
25990
25991      
25992     // private
25993     deferFocus : function(){
25994         this.focus.defer(10, this);
25995     },
25996
25997     // doc'ed in Field
25998     focus : function(){
25999         this.editorcore.focus();
26000         
26001     },
26002       
26003
26004     // private
26005     onDestroy : function(){
26006         
26007         
26008         
26009         if(this.rendered){
26010             
26011             for (var i =0; i < this.toolbars.length;i++) {
26012                 // fixme - ask toolbars for heights?
26013                 this.toolbars[i].onDestroy();
26014             }
26015             
26016             this.wrap.dom.innerHTML = '';
26017             this.wrap.remove();
26018         }
26019     },
26020
26021     // private
26022     onFirstFocus : function(){
26023         //Roo.log("onFirstFocus");
26024         this.editorcore.onFirstFocus();
26025          for (var i =0; i < this.toolbars.length;i++) {
26026             this.toolbars[i].onFirstFocus();
26027         }
26028         
26029     },
26030     
26031     // private
26032     syncValue : function()
26033     {   
26034         this.editorcore.syncValue();
26035     },
26036     
26037     pushValue : function()
26038     {   
26039         this.editorcore.pushValue();
26040     }
26041      
26042     
26043     // hide stuff that is not compatible
26044     /**
26045      * @event blur
26046      * @hide
26047      */
26048     /**
26049      * @event change
26050      * @hide
26051      */
26052     /**
26053      * @event focus
26054      * @hide
26055      */
26056     /**
26057      * @event specialkey
26058      * @hide
26059      */
26060     /**
26061      * @cfg {String} fieldClass @hide
26062      */
26063     /**
26064      * @cfg {String} focusClass @hide
26065      */
26066     /**
26067      * @cfg {String} autoCreate @hide
26068      */
26069     /**
26070      * @cfg {String} inputType @hide
26071      */
26072      
26073     /**
26074      * @cfg {String} invalidText @hide
26075      */
26076     /**
26077      * @cfg {String} msgFx @hide
26078      */
26079     /**
26080      * @cfg {String} validateOnBlur @hide
26081      */
26082 });
26083  
26084     
26085    
26086    
26087    
26088       
26089 Roo.namespace('Roo.bootstrap.htmleditor');
26090 /**
26091  * @class Roo.bootstrap.HtmlEditorToolbar1
26092  * Basic Toolbar
26093  * 
26094  * @example
26095  * Usage:
26096  *
26097  new Roo.bootstrap.HtmlEditor({
26098     ....
26099     toolbars : [
26100         new Roo.bootstrap.HtmlEditorToolbar1({
26101             disable : { fonts: 1 , format: 1, ..., ... , ...],
26102             btns : [ .... ]
26103         })
26104     }
26105      
26106  * 
26107  * @cfg {Object} disable List of elements to disable..
26108  * @cfg {Array} btns List of additional buttons.
26109  * 
26110  * 
26111  * NEEDS Extra CSS? 
26112  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26113  */
26114  
26115 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26116 {
26117     
26118     Roo.apply(this, config);
26119     
26120     // default disabled, based on 'good practice'..
26121     this.disable = this.disable || {};
26122     Roo.applyIf(this.disable, {
26123         fontSize : true,
26124         colors : true,
26125         specialElements : true
26126     });
26127     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26128     
26129     this.editor = config.editor;
26130     this.editorcore = config.editor.editorcore;
26131     
26132     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26133     
26134     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26135     // dont call parent... till later.
26136 }
26137 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26138      
26139     bar : true,
26140     
26141     editor : false,
26142     editorcore : false,
26143     
26144     
26145     formats : [
26146         "p" ,  
26147         "h1","h2","h3","h4","h5","h6", 
26148         "pre", "code", 
26149         "abbr", "acronym", "address", "cite", "samp", "var",
26150         'div','span'
26151     ],
26152     
26153     onRender : function(ct, position)
26154     {
26155        // Roo.log("Call onRender: " + this.xtype);
26156         
26157        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26158        Roo.log(this.el);
26159        this.el.dom.style.marginBottom = '0';
26160        var _this = this;
26161        var editorcore = this.editorcore;
26162        var editor= this.editor;
26163        
26164        var children = [];
26165        var btn = function(id,cmd , toggle, handler, html){
26166        
26167             var  event = toggle ? 'toggle' : 'click';
26168        
26169             var a = {
26170                 size : 'sm',
26171                 xtype: 'Button',
26172                 xns: Roo.bootstrap,
26173                 //glyphicon : id,
26174                 fa: id,
26175                 cmd : id || cmd,
26176                 enableToggle:toggle !== false,
26177                 html : html || '',
26178                 pressed : toggle ? false : null,
26179                 listeners : {}
26180             };
26181             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26182                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26183             };
26184             children.push(a);
26185             return a;
26186        }
26187        
26188     //    var cb_box = function...
26189         
26190         var style = {
26191                 xtype: 'Button',
26192                 size : 'sm',
26193                 xns: Roo.bootstrap,
26194                 fa : 'font',
26195                 //html : 'submit'
26196                 menu : {
26197                     xtype: 'Menu',
26198                     xns: Roo.bootstrap,
26199                     items:  []
26200                 }
26201         };
26202         Roo.each(this.formats, function(f) {
26203             style.menu.items.push({
26204                 xtype :'MenuItem',
26205                 xns: Roo.bootstrap,
26206                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26207                 tagname : f,
26208                 listeners : {
26209                     click : function()
26210                     {
26211                         editorcore.insertTag(this.tagname);
26212                         editor.focus();
26213                     }
26214                 }
26215                 
26216             });
26217         });
26218         children.push(style);   
26219         
26220         btn('bold',false,true);
26221         btn('italic',false,true);
26222         btn('align-left', 'justifyleft',true);
26223         btn('align-center', 'justifycenter',true);
26224         btn('align-right' , 'justifyright',true);
26225         btn('link', false, false, function(btn) {
26226             //Roo.log("create link?");
26227             var url = prompt(this.createLinkText, this.defaultLinkValue);
26228             if(url && url != 'http:/'+'/'){
26229                 this.editorcore.relayCmd('createlink', url);
26230             }
26231         }),
26232         btn('list','insertunorderedlist',true);
26233         btn('pencil', false,true, function(btn){
26234                 Roo.log(this);
26235                 this.toggleSourceEdit(btn.pressed);
26236         });
26237         
26238         if (this.editor.btns.length > 0) {
26239             for (var i = 0; i<this.editor.btns.length; i++) {
26240                 children.push(this.editor.btns[i]);
26241             }
26242         }
26243         
26244         /*
26245         var cog = {
26246                 xtype: 'Button',
26247                 size : 'sm',
26248                 xns: Roo.bootstrap,
26249                 glyphicon : 'cog',
26250                 //html : 'submit'
26251                 menu : {
26252                     xtype: 'Menu',
26253                     xns: Roo.bootstrap,
26254                     items:  []
26255                 }
26256         };
26257         
26258         cog.menu.items.push({
26259             xtype :'MenuItem',
26260             xns: Roo.bootstrap,
26261             html : Clean styles,
26262             tagname : f,
26263             listeners : {
26264                 click : function()
26265                 {
26266                     editorcore.insertTag(this.tagname);
26267                     editor.focus();
26268                 }
26269             }
26270             
26271         });
26272        */
26273         
26274          
26275        this.xtype = 'NavSimplebar';
26276         
26277         for(var i=0;i< children.length;i++) {
26278             
26279             this.buttons.add(this.addxtypeChild(children[i]));
26280             
26281         }
26282         
26283         editor.on('editorevent', this.updateToolbar, this);
26284     },
26285     onBtnClick : function(id)
26286     {
26287        this.editorcore.relayCmd(id);
26288        this.editorcore.focus();
26289     },
26290     
26291     /**
26292      * Protected method that will not generally be called directly. It triggers
26293      * a toolbar update by reading the markup state of the current selection in the editor.
26294      */
26295     updateToolbar: function(){
26296
26297         if(!this.editorcore.activated){
26298             this.editor.onFirstFocus(); // is this neeed?
26299             return;
26300         }
26301
26302         var btns = this.buttons; 
26303         var doc = this.editorcore.doc;
26304         btns.get('bold').setActive(doc.queryCommandState('bold'));
26305         btns.get('italic').setActive(doc.queryCommandState('italic'));
26306         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26307         
26308         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26309         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26310         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26311         
26312         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26313         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26314          /*
26315         
26316         var ans = this.editorcore.getAllAncestors();
26317         if (this.formatCombo) {
26318             
26319             
26320             var store = this.formatCombo.store;
26321             this.formatCombo.setValue("");
26322             for (var i =0; i < ans.length;i++) {
26323                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26324                     // select it..
26325                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26326                     break;
26327                 }
26328             }
26329         }
26330         
26331         
26332         
26333         // hides menus... - so this cant be on a menu...
26334         Roo.bootstrap.MenuMgr.hideAll();
26335         */
26336         Roo.bootstrap.MenuMgr.hideAll();
26337         //this.editorsyncValue();
26338     },
26339     onFirstFocus: function() {
26340         this.buttons.each(function(item){
26341            item.enable();
26342         });
26343     },
26344     toggleSourceEdit : function(sourceEditMode){
26345         
26346           
26347         if(sourceEditMode){
26348             Roo.log("disabling buttons");
26349            this.buttons.each( function(item){
26350                 if(item.cmd != 'pencil'){
26351                     item.disable();
26352                 }
26353             });
26354           
26355         }else{
26356             Roo.log("enabling buttons");
26357             if(this.editorcore.initialized){
26358                 this.buttons.each( function(item){
26359                     item.enable();
26360                 });
26361             }
26362             
26363         }
26364         Roo.log("calling toggole on editor");
26365         // tell the editor that it's been pressed..
26366         this.editor.toggleSourceEdit(sourceEditMode);
26367        
26368     }
26369 });
26370
26371
26372
26373
26374  
26375 /*
26376  * - LGPL
26377  */
26378
26379 /**
26380  * @class Roo.bootstrap.Markdown
26381  * @extends Roo.bootstrap.TextArea
26382  * Bootstrap Showdown editable area
26383  * @cfg {string} content
26384  * 
26385  * @constructor
26386  * Create a new Showdown
26387  */
26388
26389 Roo.bootstrap.Markdown = function(config){
26390     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26391    
26392 };
26393
26394 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26395     
26396     editing :false,
26397     
26398     initEvents : function()
26399     {
26400         
26401         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26402         this.markdownEl = this.el.createChild({
26403             cls : 'roo-markdown-area'
26404         });
26405         this.inputEl().addClass('d-none');
26406         if (this.getValue() == '') {
26407             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26408             
26409         } else {
26410             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26411         }
26412         this.markdownEl.on('click', this.toggleTextEdit, this);
26413         this.on('blur', this.toggleTextEdit, this);
26414         this.on('specialkey', this.resizeTextArea, this);
26415     },
26416     
26417     toggleTextEdit : function()
26418     {
26419         var sh = this.markdownEl.getHeight();
26420         this.inputEl().addClass('d-none');
26421         this.markdownEl.addClass('d-none');
26422         if (!this.editing) {
26423             // show editor?
26424             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26425             this.inputEl().removeClass('d-none');
26426             this.inputEl().focus();
26427             this.editing = true;
26428             return;
26429         }
26430         // show showdown...
26431         this.updateMarkdown();
26432         this.markdownEl.removeClass('d-none');
26433         this.editing = false;
26434         return;
26435     },
26436     updateMarkdown : function()
26437     {
26438         if (this.getValue() == '') {
26439             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26440             return;
26441         }
26442  
26443         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26444     },
26445     
26446     resizeTextArea: function () {
26447         
26448         var sh = 100;
26449         Roo.log([sh, this.getValue().split("\n").length * 30]);
26450         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26451     },
26452     setValue : function(val)
26453     {
26454         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26455         if (!this.editing) {
26456             this.updateMarkdown();
26457         }
26458         
26459     },
26460     focus : function()
26461     {
26462         if (!this.editing) {
26463             this.toggleTextEdit();
26464         }
26465         
26466     }
26467
26468
26469 });
26470 /**
26471  * @class Roo.bootstrap.Table.AbstractSelectionModel
26472  * @extends Roo.util.Observable
26473  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26474  * implemented by descendant classes.  This class should not be directly instantiated.
26475  * @constructor
26476  */
26477 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26478     this.locked = false;
26479     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26480 };
26481
26482
26483 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26484     /** @ignore Called by the grid automatically. Do not call directly. */
26485     init : function(grid){
26486         this.grid = grid;
26487         this.initEvents();
26488     },
26489
26490     /**
26491      * Locks the selections.
26492      */
26493     lock : function(){
26494         this.locked = true;
26495     },
26496
26497     /**
26498      * Unlocks the selections.
26499      */
26500     unlock : function(){
26501         this.locked = false;
26502     },
26503
26504     /**
26505      * Returns true if the selections are locked.
26506      * @return {Boolean}
26507      */
26508     isLocked : function(){
26509         return this.locked;
26510     },
26511     
26512     
26513     initEvents : function ()
26514     {
26515         
26516     }
26517 });
26518 /**
26519  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26520  * @class Roo.bootstrap.Table.RowSelectionModel
26521  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26522  * It supports multiple selections and keyboard selection/navigation. 
26523  * @constructor
26524  * @param {Object} config
26525  */
26526
26527 Roo.bootstrap.Table.RowSelectionModel = function(config){
26528     Roo.apply(this, config);
26529     this.selections = new Roo.util.MixedCollection(false, function(o){
26530         return o.id;
26531     });
26532
26533     this.last = false;
26534     this.lastActive = false;
26535
26536     this.addEvents({
26537         /**
26538              * @event selectionchange
26539              * Fires when the selection changes
26540              * @param {SelectionModel} this
26541              */
26542             "selectionchange" : true,
26543         /**
26544              * @event afterselectionchange
26545              * Fires after the selection changes (eg. by key press or clicking)
26546              * @param {SelectionModel} this
26547              */
26548             "afterselectionchange" : true,
26549         /**
26550              * @event beforerowselect
26551              * Fires when a row is selected being selected, return false to cancel.
26552              * @param {SelectionModel} this
26553              * @param {Number} rowIndex The selected index
26554              * @param {Boolean} keepExisting False if other selections will be cleared
26555              */
26556             "beforerowselect" : true,
26557         /**
26558              * @event rowselect
26559              * Fires when a row is selected.
26560              * @param {SelectionModel} this
26561              * @param {Number} rowIndex The selected index
26562              * @param {Roo.data.Record} r The record
26563              */
26564             "rowselect" : true,
26565         /**
26566              * @event rowdeselect
26567              * Fires when a row is deselected.
26568              * @param {SelectionModel} this
26569              * @param {Number} rowIndex The selected index
26570              */
26571         "rowdeselect" : true
26572     });
26573     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26574     this.locked = false;
26575  };
26576
26577 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26578     /**
26579      * @cfg {Boolean} singleSelect
26580      * True to allow selection of only one row at a time (defaults to false)
26581      */
26582     singleSelect : false,
26583
26584     // private
26585     initEvents : function()
26586     {
26587
26588         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26589         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26590         //}else{ // allow click to work like normal
26591          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26592         //}
26593         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26594         this.grid.on("rowclick", this.handleMouseDown, this);
26595         
26596         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26597             "up" : function(e){
26598                 if(!e.shiftKey){
26599                     this.selectPrevious(e.shiftKey);
26600                 }else if(this.last !== false && this.lastActive !== false){
26601                     var last = this.last;
26602                     this.selectRange(this.last,  this.lastActive-1);
26603                     this.grid.getView().focusRow(this.lastActive);
26604                     if(last !== false){
26605                         this.last = last;
26606                     }
26607                 }else{
26608                     this.selectFirstRow();
26609                 }
26610                 this.fireEvent("afterselectionchange", this);
26611             },
26612             "down" : function(e){
26613                 if(!e.shiftKey){
26614                     this.selectNext(e.shiftKey);
26615                 }else if(this.last !== false && this.lastActive !== false){
26616                     var last = this.last;
26617                     this.selectRange(this.last,  this.lastActive+1);
26618                     this.grid.getView().focusRow(this.lastActive);
26619                     if(last !== false){
26620                         this.last = last;
26621                     }
26622                 }else{
26623                     this.selectFirstRow();
26624                 }
26625                 this.fireEvent("afterselectionchange", this);
26626             },
26627             scope: this
26628         });
26629         this.grid.store.on('load', function(){
26630             this.selections.clear();
26631         },this);
26632         /*
26633         var view = this.grid.view;
26634         view.on("refresh", this.onRefresh, this);
26635         view.on("rowupdated", this.onRowUpdated, this);
26636         view.on("rowremoved", this.onRemove, this);
26637         */
26638     },
26639
26640     // private
26641     onRefresh : function()
26642     {
26643         var ds = this.grid.store, i, v = this.grid.view;
26644         var s = this.selections;
26645         s.each(function(r){
26646             if((i = ds.indexOfId(r.id)) != -1){
26647                 v.onRowSelect(i);
26648             }else{
26649                 s.remove(r);
26650             }
26651         });
26652     },
26653
26654     // private
26655     onRemove : function(v, index, r){
26656         this.selections.remove(r);
26657     },
26658
26659     // private
26660     onRowUpdated : function(v, index, r){
26661         if(this.isSelected(r)){
26662             v.onRowSelect(index);
26663         }
26664     },
26665
26666     /**
26667      * Select records.
26668      * @param {Array} records The records to select
26669      * @param {Boolean} keepExisting (optional) True to keep existing selections
26670      */
26671     selectRecords : function(records, keepExisting)
26672     {
26673         if(!keepExisting){
26674             this.clearSelections();
26675         }
26676             var ds = this.grid.store;
26677         for(var i = 0, len = records.length; i < len; i++){
26678             this.selectRow(ds.indexOf(records[i]), true);
26679         }
26680     },
26681
26682     /**
26683      * Gets the number of selected rows.
26684      * @return {Number}
26685      */
26686     getCount : function(){
26687         return this.selections.length;
26688     },
26689
26690     /**
26691      * Selects the first row in the grid.
26692      */
26693     selectFirstRow : function(){
26694         this.selectRow(0);
26695     },
26696
26697     /**
26698      * Select the last row.
26699      * @param {Boolean} keepExisting (optional) True to keep existing selections
26700      */
26701     selectLastRow : function(keepExisting){
26702         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26703         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26704     },
26705
26706     /**
26707      * Selects the row immediately following the last selected row.
26708      * @param {Boolean} keepExisting (optional) True to keep existing selections
26709      */
26710     selectNext : function(keepExisting)
26711     {
26712             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26713             this.selectRow(this.last+1, keepExisting);
26714             this.grid.getView().focusRow(this.last);
26715         }
26716     },
26717
26718     /**
26719      * Selects the row that precedes the last selected row.
26720      * @param {Boolean} keepExisting (optional) True to keep existing selections
26721      */
26722     selectPrevious : function(keepExisting){
26723         if(this.last){
26724             this.selectRow(this.last-1, keepExisting);
26725             this.grid.getView().focusRow(this.last);
26726         }
26727     },
26728
26729     /**
26730      * Returns the selected records
26731      * @return {Array} Array of selected records
26732      */
26733     getSelections : function(){
26734         return [].concat(this.selections.items);
26735     },
26736
26737     /**
26738      * Returns the first selected record.
26739      * @return {Record}
26740      */
26741     getSelected : function(){
26742         return this.selections.itemAt(0);
26743     },
26744
26745
26746     /**
26747      * Clears all selections.
26748      */
26749     clearSelections : function(fast)
26750     {
26751         if(this.locked) {
26752             return;
26753         }
26754         if(fast !== true){
26755                 var ds = this.grid.store;
26756             var s = this.selections;
26757             s.each(function(r){
26758                 this.deselectRow(ds.indexOfId(r.id));
26759             }, this);
26760             s.clear();
26761         }else{
26762             this.selections.clear();
26763         }
26764         this.last = false;
26765     },
26766
26767
26768     /**
26769      * Selects all rows.
26770      */
26771     selectAll : function(){
26772         if(this.locked) {
26773             return;
26774         }
26775         this.selections.clear();
26776         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26777             this.selectRow(i, true);
26778         }
26779     },
26780
26781     /**
26782      * Returns True if there is a selection.
26783      * @return {Boolean}
26784      */
26785     hasSelection : function(){
26786         return this.selections.length > 0;
26787     },
26788
26789     /**
26790      * Returns True if the specified row is selected.
26791      * @param {Number/Record} record The record or index of the record to check
26792      * @return {Boolean}
26793      */
26794     isSelected : function(index){
26795             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26796         return (r && this.selections.key(r.id) ? true : false);
26797     },
26798
26799     /**
26800      * Returns True if the specified record id is selected.
26801      * @param {String} id The id of record to check
26802      * @return {Boolean}
26803      */
26804     isIdSelected : function(id){
26805         return (this.selections.key(id) ? true : false);
26806     },
26807
26808
26809     // private
26810     handleMouseDBClick : function(e, t){
26811         
26812     },
26813     // private
26814     handleMouseDown : function(e, t)
26815     {
26816             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26817         if(this.isLocked() || rowIndex < 0 ){
26818             return;
26819         };
26820         if(e.shiftKey && this.last !== false){
26821             var last = this.last;
26822             this.selectRange(last, rowIndex, e.ctrlKey);
26823             this.last = last; // reset the last
26824             t.focus();
26825     
26826         }else{
26827             var isSelected = this.isSelected(rowIndex);
26828             //Roo.log("select row:" + rowIndex);
26829             if(isSelected){
26830                 this.deselectRow(rowIndex);
26831             } else {
26832                         this.selectRow(rowIndex, true);
26833             }
26834     
26835             /*
26836                 if(e.button !== 0 && isSelected){
26837                 alert('rowIndex 2: ' + rowIndex);
26838                     view.focusRow(rowIndex);
26839                 }else if(e.ctrlKey && isSelected){
26840                     this.deselectRow(rowIndex);
26841                 }else if(!isSelected){
26842                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26843                     view.focusRow(rowIndex);
26844                 }
26845             */
26846         }
26847         this.fireEvent("afterselectionchange", this);
26848     },
26849     // private
26850     handleDragableRowClick :  function(grid, rowIndex, e) 
26851     {
26852         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26853             this.selectRow(rowIndex, false);
26854             grid.view.focusRow(rowIndex);
26855              this.fireEvent("afterselectionchange", this);
26856         }
26857     },
26858     
26859     /**
26860      * Selects multiple rows.
26861      * @param {Array} rows Array of the indexes of the row to select
26862      * @param {Boolean} keepExisting (optional) True to keep existing selections
26863      */
26864     selectRows : function(rows, keepExisting){
26865         if(!keepExisting){
26866             this.clearSelections();
26867         }
26868         for(var i = 0, len = rows.length; i < len; i++){
26869             this.selectRow(rows[i], true);
26870         }
26871     },
26872
26873     /**
26874      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26875      * @param {Number} startRow The index of the first row in the range
26876      * @param {Number} endRow The index of the last row in the range
26877      * @param {Boolean} keepExisting (optional) True to retain existing selections
26878      */
26879     selectRange : function(startRow, endRow, keepExisting){
26880         if(this.locked) {
26881             return;
26882         }
26883         if(!keepExisting){
26884             this.clearSelections();
26885         }
26886         if(startRow <= endRow){
26887             for(var i = startRow; i <= endRow; i++){
26888                 this.selectRow(i, true);
26889             }
26890         }else{
26891             for(var i = startRow; i >= endRow; i--){
26892                 this.selectRow(i, true);
26893             }
26894         }
26895     },
26896
26897     /**
26898      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26899      * @param {Number} startRow The index of the first row in the range
26900      * @param {Number} endRow The index of the last row in the range
26901      */
26902     deselectRange : function(startRow, endRow, preventViewNotify){
26903         if(this.locked) {
26904             return;
26905         }
26906         for(var i = startRow; i <= endRow; i++){
26907             this.deselectRow(i, preventViewNotify);
26908         }
26909     },
26910
26911     /**
26912      * Selects a row.
26913      * @param {Number} row The index of the row to select
26914      * @param {Boolean} keepExisting (optional) True to keep existing selections
26915      */
26916     selectRow : function(index, keepExisting, preventViewNotify)
26917     {
26918             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26919             return;
26920         }
26921         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26922             if(!keepExisting || this.singleSelect){
26923                 this.clearSelections();
26924             }
26925             
26926             var r = this.grid.store.getAt(index);
26927             //console.log('selectRow - record id :' + r.id);
26928             
26929             this.selections.add(r);
26930             this.last = this.lastActive = index;
26931             if(!preventViewNotify){
26932                 var proxy = new Roo.Element(
26933                                 this.grid.getRowDom(index)
26934                 );
26935                 proxy.addClass('bg-info info');
26936             }
26937             this.fireEvent("rowselect", this, index, r);
26938             this.fireEvent("selectionchange", this);
26939         }
26940     },
26941
26942     /**
26943      * Deselects a row.
26944      * @param {Number} row The index of the row to deselect
26945      */
26946     deselectRow : function(index, preventViewNotify)
26947     {
26948         if(this.locked) {
26949             return;
26950         }
26951         if(this.last == index){
26952             this.last = false;
26953         }
26954         if(this.lastActive == index){
26955             this.lastActive = false;
26956         }
26957         
26958         var r = this.grid.store.getAt(index);
26959         if (!r) {
26960             return;
26961         }
26962         
26963         this.selections.remove(r);
26964         //.console.log('deselectRow - record id :' + r.id);
26965         if(!preventViewNotify){
26966         
26967             var proxy = new Roo.Element(
26968                 this.grid.getRowDom(index)
26969             );
26970             proxy.removeClass('bg-info info');
26971         }
26972         this.fireEvent("rowdeselect", this, index);
26973         this.fireEvent("selectionchange", this);
26974     },
26975
26976     // private
26977     restoreLast : function(){
26978         if(this._last){
26979             this.last = this._last;
26980         }
26981     },
26982
26983     // private
26984     acceptsNav : function(row, col, cm){
26985         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26986     },
26987
26988     // private
26989     onEditorKey : function(field, e){
26990         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
26991         if(k == e.TAB){
26992             e.stopEvent();
26993             ed.completeEdit();
26994             if(e.shiftKey){
26995                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
26996             }else{
26997                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
26998             }
26999         }else if(k == e.ENTER && !e.ctrlKey){
27000             e.stopEvent();
27001             ed.completeEdit();
27002             if(e.shiftKey){
27003                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27004             }else{
27005                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27006             }
27007         }else if(k == e.ESC){
27008             ed.cancelEdit();
27009         }
27010         if(newCell){
27011             g.startEditing(newCell[0], newCell[1]);
27012         }
27013     }
27014 });
27015 /*
27016  * Based on:
27017  * Ext JS Library 1.1.1
27018  * Copyright(c) 2006-2007, Ext JS, LLC.
27019  *
27020  * Originally Released Under LGPL - original licence link has changed is not relivant.
27021  *
27022  * Fork - LGPL
27023  * <script type="text/javascript">
27024  */
27025  
27026 /**
27027  * @class Roo.bootstrap.PagingToolbar
27028  * @extends Roo.bootstrap.NavSimplebar
27029  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27030  * @constructor
27031  * Create a new PagingToolbar
27032  * @param {Object} config The config object
27033  * @param {Roo.data.Store} store
27034  */
27035 Roo.bootstrap.PagingToolbar = function(config)
27036 {
27037     // old args format still supported... - xtype is prefered..
27038         // created from xtype...
27039     
27040     this.ds = config.dataSource;
27041     
27042     if (config.store && !this.ds) {
27043         this.store= Roo.factory(config.store, Roo.data);
27044         this.ds = this.store;
27045         this.ds.xmodule = this.xmodule || false;
27046     }
27047     
27048     this.toolbarItems = [];
27049     if (config.items) {
27050         this.toolbarItems = config.items;
27051     }
27052     
27053     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27054     
27055     this.cursor = 0;
27056     
27057     if (this.ds) { 
27058         this.bind(this.ds);
27059     }
27060     
27061     if (Roo.bootstrap.version == 4) {
27062         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27063     } else {
27064         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27065     }
27066     
27067 };
27068
27069 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27070     /**
27071      * @cfg {Roo.data.Store} dataSource
27072      * The underlying data store providing the paged data
27073      */
27074     /**
27075      * @cfg {String/HTMLElement/Element} container
27076      * container The id or element that will contain the toolbar
27077      */
27078     /**
27079      * @cfg {Boolean} displayInfo
27080      * True to display the displayMsg (defaults to false)
27081      */
27082     /**
27083      * @cfg {Number} pageSize
27084      * The number of records to display per page (defaults to 20)
27085      */
27086     pageSize: 20,
27087     /**
27088      * @cfg {String} displayMsg
27089      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27090      */
27091     displayMsg : 'Displaying {0} - {1} of {2}',
27092     /**
27093      * @cfg {String} emptyMsg
27094      * The message to display when no records are found (defaults to "No data to display")
27095      */
27096     emptyMsg : 'No data to display',
27097     /**
27098      * Customizable piece of the default paging text (defaults to "Page")
27099      * @type String
27100      */
27101     beforePageText : "Page",
27102     /**
27103      * Customizable piece of the default paging text (defaults to "of %0")
27104      * @type String
27105      */
27106     afterPageText : "of {0}",
27107     /**
27108      * Customizable piece of the default paging text (defaults to "First Page")
27109      * @type String
27110      */
27111     firstText : "First Page",
27112     /**
27113      * Customizable piece of the default paging text (defaults to "Previous Page")
27114      * @type String
27115      */
27116     prevText : "Previous Page",
27117     /**
27118      * Customizable piece of the default paging text (defaults to "Next Page")
27119      * @type String
27120      */
27121     nextText : "Next Page",
27122     /**
27123      * Customizable piece of the default paging text (defaults to "Last Page")
27124      * @type String
27125      */
27126     lastText : "Last Page",
27127     /**
27128      * Customizable piece of the default paging text (defaults to "Refresh")
27129      * @type String
27130      */
27131     refreshText : "Refresh",
27132
27133     buttons : false,
27134     // private
27135     onRender : function(ct, position) 
27136     {
27137         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27138         this.navgroup.parentId = this.id;
27139         this.navgroup.onRender(this.el, null);
27140         // add the buttons to the navgroup
27141         
27142         if(this.displayInfo){
27143             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27144             this.displayEl = this.el.select('.x-paging-info', true).first();
27145 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27146 //            this.displayEl = navel.el.select('span',true).first();
27147         }
27148         
27149         var _this = this;
27150         
27151         if(this.buttons){
27152             Roo.each(_this.buttons, function(e){ // this might need to use render????
27153                Roo.factory(e).render(_this.el);
27154             });
27155         }
27156             
27157         Roo.each(_this.toolbarItems, function(e) {
27158             _this.navgroup.addItem(e);
27159         });
27160         
27161         
27162         this.first = this.navgroup.addItem({
27163             tooltip: this.firstText,
27164             cls: "prev btn-outline-secondary",
27165             html : ' <i class="fa fa-step-backward"></i>',
27166             disabled: true,
27167             preventDefault: true,
27168             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27169         });
27170         
27171         this.prev =  this.navgroup.addItem({
27172             tooltip: this.prevText,
27173             cls: "prev btn-outline-secondary",
27174             html : ' <i class="fa fa-backward"></i>',
27175             disabled: true,
27176             preventDefault: true,
27177             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27178         });
27179     //this.addSeparator();
27180         
27181         
27182         var field = this.navgroup.addItem( {
27183             tagtype : 'span',
27184             cls : 'x-paging-position  btn-outline-secondary',
27185              disabled: true,
27186             html : this.beforePageText  +
27187                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27188                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27189          } ); //?? escaped?
27190         
27191         this.field = field.el.select('input', true).first();
27192         this.field.on("keydown", this.onPagingKeydown, this);
27193         this.field.on("focus", function(){this.dom.select();});
27194     
27195     
27196         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27197         //this.field.setHeight(18);
27198         //this.addSeparator();
27199         this.next = this.navgroup.addItem({
27200             tooltip: this.nextText,
27201             cls: "next btn-outline-secondary",
27202             html : ' <i class="fa fa-forward"></i>',
27203             disabled: true,
27204             preventDefault: true,
27205             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27206         });
27207         this.last = this.navgroup.addItem({
27208             tooltip: this.lastText,
27209             html : ' <i class="fa fa-step-forward"></i>',
27210             cls: "next btn-outline-secondary",
27211             disabled: true,
27212             preventDefault: true,
27213             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27214         });
27215     //this.addSeparator();
27216         this.loading = this.navgroup.addItem({
27217             tooltip: this.refreshText,
27218             cls: "btn-outline-secondary",
27219             html : ' <i class="fa fa-refresh"></i>',
27220             preventDefault: true,
27221             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27222         });
27223         
27224     },
27225
27226     // private
27227     updateInfo : function(){
27228         if(this.displayEl){
27229             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27230             var msg = count == 0 ?
27231                 this.emptyMsg :
27232                 String.format(
27233                     this.displayMsg,
27234                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27235                 );
27236             this.displayEl.update(msg);
27237         }
27238     },
27239
27240     // private
27241     onLoad : function(ds, r, o)
27242     {
27243         this.cursor = o.params.start ? o.params.start : 0;
27244         
27245         var d = this.getPageData(),
27246             ap = d.activePage,
27247             ps = d.pages;
27248         
27249         
27250         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27251         this.field.dom.value = ap;
27252         this.first.setDisabled(ap == 1);
27253         this.prev.setDisabled(ap == 1);
27254         this.next.setDisabled(ap == ps);
27255         this.last.setDisabled(ap == ps);
27256         this.loading.enable();
27257         this.updateInfo();
27258     },
27259
27260     // private
27261     getPageData : function(){
27262         var total = this.ds.getTotalCount();
27263         return {
27264             total : total,
27265             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27266             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27267         };
27268     },
27269
27270     // private
27271     onLoadError : function(){
27272         this.loading.enable();
27273     },
27274
27275     // private
27276     onPagingKeydown : function(e){
27277         var k = e.getKey();
27278         var d = this.getPageData();
27279         if(k == e.RETURN){
27280             var v = this.field.dom.value, pageNum;
27281             if(!v || isNaN(pageNum = parseInt(v, 10))){
27282                 this.field.dom.value = d.activePage;
27283                 return;
27284             }
27285             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27286             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27287             e.stopEvent();
27288         }
27289         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))
27290         {
27291           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27292           this.field.dom.value = pageNum;
27293           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27294           e.stopEvent();
27295         }
27296         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27297         {
27298           var v = this.field.dom.value, pageNum; 
27299           var increment = (e.shiftKey) ? 10 : 1;
27300           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27301                 increment *= -1;
27302           }
27303           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27304             this.field.dom.value = d.activePage;
27305             return;
27306           }
27307           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27308           {
27309             this.field.dom.value = parseInt(v, 10) + increment;
27310             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27311             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27312           }
27313           e.stopEvent();
27314         }
27315     },
27316
27317     // private
27318     beforeLoad : function(){
27319         if(this.loading){
27320             this.loading.disable();
27321         }
27322     },
27323
27324     // private
27325     onClick : function(which){
27326         
27327         var ds = this.ds;
27328         if (!ds) {
27329             return;
27330         }
27331         
27332         switch(which){
27333             case "first":
27334                 ds.load({params:{start: 0, limit: this.pageSize}});
27335             break;
27336             case "prev":
27337                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27338             break;
27339             case "next":
27340                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27341             break;
27342             case "last":
27343                 var total = ds.getTotalCount();
27344                 var extra = total % this.pageSize;
27345                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27346                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27347             break;
27348             case "refresh":
27349                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27350             break;
27351         }
27352     },
27353
27354     /**
27355      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27356      * @param {Roo.data.Store} store The data store to unbind
27357      */
27358     unbind : function(ds){
27359         ds.un("beforeload", this.beforeLoad, this);
27360         ds.un("load", this.onLoad, this);
27361         ds.un("loadexception", this.onLoadError, this);
27362         ds.un("remove", this.updateInfo, this);
27363         ds.un("add", this.updateInfo, this);
27364         this.ds = undefined;
27365     },
27366
27367     /**
27368      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27369      * @param {Roo.data.Store} store The data store to bind
27370      */
27371     bind : function(ds){
27372         ds.on("beforeload", this.beforeLoad, this);
27373         ds.on("load", this.onLoad, this);
27374         ds.on("loadexception", this.onLoadError, this);
27375         ds.on("remove", this.updateInfo, this);
27376         ds.on("add", this.updateInfo, this);
27377         this.ds = ds;
27378     }
27379 });/*
27380  * - LGPL
27381  *
27382  * element
27383  * 
27384  */
27385
27386 /**
27387  * @class Roo.bootstrap.MessageBar
27388  * @extends Roo.bootstrap.Component
27389  * Bootstrap MessageBar class
27390  * @cfg {String} html contents of the MessageBar
27391  * @cfg {String} weight (info | success | warning | danger) default info
27392  * @cfg {String} beforeClass insert the bar before the given class
27393  * @cfg {Boolean} closable (true | false) default false
27394  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27395  * 
27396  * @constructor
27397  * Create a new Element
27398  * @param {Object} config The config object
27399  */
27400
27401 Roo.bootstrap.MessageBar = function(config){
27402     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27403 };
27404
27405 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27406     
27407     html: '',
27408     weight: 'info',
27409     closable: false,
27410     fixed: false,
27411     beforeClass: 'bootstrap-sticky-wrap',
27412     
27413     getAutoCreate : function(){
27414         
27415         var cfg = {
27416             tag: 'div',
27417             cls: 'alert alert-dismissable alert-' + this.weight,
27418             cn: [
27419                 {
27420                     tag: 'span',
27421                     cls: 'message',
27422                     html: this.html || ''
27423                 }
27424             ]
27425         };
27426         
27427         if(this.fixed){
27428             cfg.cls += ' alert-messages-fixed';
27429         }
27430         
27431         if(this.closable){
27432             cfg.cn.push({
27433                 tag: 'button',
27434                 cls: 'close',
27435                 html: 'x'
27436             });
27437         }
27438         
27439         return cfg;
27440     },
27441     
27442     onRender : function(ct, position)
27443     {
27444         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27445         
27446         if(!this.el){
27447             var cfg = Roo.apply({},  this.getAutoCreate());
27448             cfg.id = Roo.id();
27449             
27450             if (this.cls) {
27451                 cfg.cls += ' ' + this.cls;
27452             }
27453             if (this.style) {
27454                 cfg.style = this.style;
27455             }
27456             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27457             
27458             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27459         }
27460         
27461         this.el.select('>button.close').on('click', this.hide, this);
27462         
27463     },
27464     
27465     show : function()
27466     {
27467         if (!this.rendered) {
27468             this.render();
27469         }
27470         
27471         this.el.show();
27472         
27473         this.fireEvent('show', this);
27474         
27475     },
27476     
27477     hide : function()
27478     {
27479         if (!this.rendered) {
27480             this.render();
27481         }
27482         
27483         this.el.hide();
27484         
27485         this.fireEvent('hide', this);
27486     },
27487     
27488     update : function()
27489     {
27490 //        var e = this.el.dom.firstChild;
27491 //        
27492 //        if(this.closable){
27493 //            e = e.nextSibling;
27494 //        }
27495 //        
27496 //        e.data = this.html || '';
27497
27498         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27499     }
27500    
27501 });
27502
27503  
27504
27505      /*
27506  * - LGPL
27507  *
27508  * Graph
27509  * 
27510  */
27511
27512
27513 /**
27514  * @class Roo.bootstrap.Graph
27515  * @extends Roo.bootstrap.Component
27516  * Bootstrap Graph class
27517 > Prameters
27518  -sm {number} sm 4
27519  -md {number} md 5
27520  @cfg {String} graphtype  bar | vbar | pie
27521  @cfg {number} g_x coodinator | centre x (pie)
27522  @cfg {number} g_y coodinator | centre y (pie)
27523  @cfg {number} g_r radius (pie)
27524  @cfg {number} g_height height of the chart (respected by all elements in the set)
27525  @cfg {number} g_width width of the chart (respected by all elements in the set)
27526  @cfg {Object} title The title of the chart
27527     
27528  -{Array}  values
27529  -opts (object) options for the chart 
27530      o {
27531      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27532      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27533      o vgutter (number)
27534      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.
27535      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27536      o to
27537      o stretch (boolean)
27538      o }
27539  -opts (object) options for the pie
27540      o{
27541      o cut
27542      o startAngle (number)
27543      o endAngle (number)
27544      } 
27545  *
27546  * @constructor
27547  * Create a new Input
27548  * @param {Object} config The config object
27549  */
27550
27551 Roo.bootstrap.Graph = function(config){
27552     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27553     
27554     this.addEvents({
27555         // img events
27556         /**
27557          * @event click
27558          * The img click event for the img.
27559          * @param {Roo.EventObject} e
27560          */
27561         "click" : true
27562     });
27563 };
27564
27565 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27566     
27567     sm: 4,
27568     md: 5,
27569     graphtype: 'bar',
27570     g_height: 250,
27571     g_width: 400,
27572     g_x: 50,
27573     g_y: 50,
27574     g_r: 30,
27575     opts:{
27576         //g_colors: this.colors,
27577         g_type: 'soft',
27578         g_gutter: '20%'
27579
27580     },
27581     title : false,
27582
27583     getAutoCreate : function(){
27584         
27585         var cfg = {
27586             tag: 'div',
27587             html : null
27588         };
27589         
27590         
27591         return  cfg;
27592     },
27593
27594     onRender : function(ct,position){
27595         
27596         
27597         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27598         
27599         if (typeof(Raphael) == 'undefined') {
27600             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27601             return;
27602         }
27603         
27604         this.raphael = Raphael(this.el.dom);
27605         
27606                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27607                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27608                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27609                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27610                 /*
27611                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27612                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27613                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27614                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27615                 
27616                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27617                 r.barchart(330, 10, 300, 220, data1);
27618                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27619                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27620                 */
27621                 
27622                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27623                 // r.barchart(30, 30, 560, 250,  xdata, {
27624                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27625                 //     axis : "0 0 1 1",
27626                 //     axisxlabels :  xdata
27627                 //     //yvalues : cols,
27628                    
27629                 // });
27630 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27631 //        
27632 //        this.load(null,xdata,{
27633 //                axis : "0 0 1 1",
27634 //                axisxlabels :  xdata
27635 //                });
27636
27637     },
27638
27639     load : function(graphtype,xdata,opts)
27640     {
27641         this.raphael.clear();
27642         if(!graphtype) {
27643             graphtype = this.graphtype;
27644         }
27645         if(!opts){
27646             opts = this.opts;
27647         }
27648         var r = this.raphael,
27649             fin = function () {
27650                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27651             },
27652             fout = function () {
27653                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27654             },
27655             pfin = function() {
27656                 this.sector.stop();
27657                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27658
27659                 if (this.label) {
27660                     this.label[0].stop();
27661                     this.label[0].attr({ r: 7.5 });
27662                     this.label[1].attr({ "font-weight": 800 });
27663                 }
27664             },
27665             pfout = function() {
27666                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27667
27668                 if (this.label) {
27669                     this.label[0].animate({ r: 5 }, 500, "bounce");
27670                     this.label[1].attr({ "font-weight": 400 });
27671                 }
27672             };
27673
27674         switch(graphtype){
27675             case 'bar':
27676                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27677                 break;
27678             case 'hbar':
27679                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27680                 break;
27681             case 'pie':
27682 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27683 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27684 //            
27685                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27686                 
27687                 break;
27688
27689         }
27690         
27691         if(this.title){
27692             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27693         }
27694         
27695     },
27696     
27697     setTitle: function(o)
27698     {
27699         this.title = o;
27700     },
27701     
27702     initEvents: function() {
27703         
27704         if(!this.href){
27705             this.el.on('click', this.onClick, this);
27706         }
27707     },
27708     
27709     onClick : function(e)
27710     {
27711         Roo.log('img onclick');
27712         this.fireEvent('click', this, e);
27713     }
27714    
27715 });
27716
27717  
27718 /*
27719  * - LGPL
27720  *
27721  * numberBox
27722  * 
27723  */
27724 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27725
27726 /**
27727  * @class Roo.bootstrap.dash.NumberBox
27728  * @extends Roo.bootstrap.Component
27729  * Bootstrap NumberBox class
27730  * @cfg {String} headline Box headline
27731  * @cfg {String} content Box content
27732  * @cfg {String} icon Box icon
27733  * @cfg {String} footer Footer text
27734  * @cfg {String} fhref Footer href
27735  * 
27736  * @constructor
27737  * Create a new NumberBox
27738  * @param {Object} config The config object
27739  */
27740
27741
27742 Roo.bootstrap.dash.NumberBox = function(config){
27743     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27744     
27745 };
27746
27747 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27748     
27749     headline : '',
27750     content : '',
27751     icon : '',
27752     footer : '',
27753     fhref : '',
27754     ficon : '',
27755     
27756     getAutoCreate : function(){
27757         
27758         var cfg = {
27759             tag : 'div',
27760             cls : 'small-box ',
27761             cn : [
27762                 {
27763                     tag : 'div',
27764                     cls : 'inner',
27765                     cn :[
27766                         {
27767                             tag : 'h3',
27768                             cls : 'roo-headline',
27769                             html : this.headline
27770                         },
27771                         {
27772                             tag : 'p',
27773                             cls : 'roo-content',
27774                             html : this.content
27775                         }
27776                     ]
27777                 }
27778             ]
27779         };
27780         
27781         if(this.icon){
27782             cfg.cn.push({
27783                 tag : 'div',
27784                 cls : 'icon',
27785                 cn :[
27786                     {
27787                         tag : 'i',
27788                         cls : 'ion ' + this.icon
27789                     }
27790                 ]
27791             });
27792         }
27793         
27794         if(this.footer){
27795             var footer = {
27796                 tag : 'a',
27797                 cls : 'small-box-footer',
27798                 href : this.fhref || '#',
27799                 html : this.footer
27800             };
27801             
27802             cfg.cn.push(footer);
27803             
27804         }
27805         
27806         return  cfg;
27807     },
27808
27809     onRender : function(ct,position){
27810         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27811
27812
27813        
27814                 
27815     },
27816
27817     setHeadline: function (value)
27818     {
27819         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27820     },
27821     
27822     setFooter: function (value, href)
27823     {
27824         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27825         
27826         if(href){
27827             this.el.select('a.small-box-footer',true).first().attr('href', href);
27828         }
27829         
27830     },
27831
27832     setContent: function (value)
27833     {
27834         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27835     },
27836
27837     initEvents: function() 
27838     {   
27839         
27840     }
27841     
27842 });
27843
27844  
27845 /*
27846  * - LGPL
27847  *
27848  * TabBox
27849  * 
27850  */
27851 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27852
27853 /**
27854  * @class Roo.bootstrap.dash.TabBox
27855  * @extends Roo.bootstrap.Component
27856  * Bootstrap TabBox class
27857  * @cfg {String} title Title of the TabBox
27858  * @cfg {String} icon Icon of the TabBox
27859  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27860  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27861  * 
27862  * @constructor
27863  * Create a new TabBox
27864  * @param {Object} config The config object
27865  */
27866
27867
27868 Roo.bootstrap.dash.TabBox = function(config){
27869     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27870     this.addEvents({
27871         // raw events
27872         /**
27873          * @event addpane
27874          * When a pane is added
27875          * @param {Roo.bootstrap.dash.TabPane} pane
27876          */
27877         "addpane" : true,
27878         /**
27879          * @event activatepane
27880          * When a pane is activated
27881          * @param {Roo.bootstrap.dash.TabPane} pane
27882          */
27883         "activatepane" : true
27884         
27885          
27886     });
27887     
27888     this.panes = [];
27889 };
27890
27891 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27892
27893     title : '',
27894     icon : false,
27895     showtabs : true,
27896     tabScrollable : false,
27897     
27898     getChildContainer : function()
27899     {
27900         return this.el.select('.tab-content', true).first();
27901     },
27902     
27903     getAutoCreate : function(){
27904         
27905         var header = {
27906             tag: 'li',
27907             cls: 'pull-left header',
27908             html: this.title,
27909             cn : []
27910         };
27911         
27912         if(this.icon){
27913             header.cn.push({
27914                 tag: 'i',
27915                 cls: 'fa ' + this.icon
27916             });
27917         }
27918         
27919         var h = {
27920             tag: 'ul',
27921             cls: 'nav nav-tabs pull-right',
27922             cn: [
27923                 header
27924             ]
27925         };
27926         
27927         if(this.tabScrollable){
27928             h = {
27929                 tag: 'div',
27930                 cls: 'tab-header',
27931                 cn: [
27932                     {
27933                         tag: 'ul',
27934                         cls: 'nav nav-tabs pull-right',
27935                         cn: [
27936                             header
27937                         ]
27938                     }
27939                 ]
27940             };
27941         }
27942         
27943         var cfg = {
27944             tag: 'div',
27945             cls: 'nav-tabs-custom',
27946             cn: [
27947                 h,
27948                 {
27949                     tag: 'div',
27950                     cls: 'tab-content no-padding',
27951                     cn: []
27952                 }
27953             ]
27954         };
27955
27956         return  cfg;
27957     },
27958     initEvents : function()
27959     {
27960         //Roo.log('add add pane handler');
27961         this.on('addpane', this.onAddPane, this);
27962     },
27963      /**
27964      * Updates the box title
27965      * @param {String} html to set the title to.
27966      */
27967     setTitle : function(value)
27968     {
27969         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27970     },
27971     onAddPane : function(pane)
27972     {
27973         this.panes.push(pane);
27974         //Roo.log('addpane');
27975         //Roo.log(pane);
27976         // tabs are rendere left to right..
27977         if(!this.showtabs){
27978             return;
27979         }
27980         
27981         var ctr = this.el.select('.nav-tabs', true).first();
27982          
27983          
27984         var existing = ctr.select('.nav-tab',true);
27985         var qty = existing.getCount();;
27986         
27987         
27988         var tab = ctr.createChild({
27989             tag : 'li',
27990             cls : 'nav-tab' + (qty ? '' : ' active'),
27991             cn : [
27992                 {
27993                     tag : 'a',
27994                     href:'#',
27995                     html : pane.title
27996                 }
27997             ]
27998         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
27999         pane.tab = tab;
28000         
28001         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28002         if (!qty) {
28003             pane.el.addClass('active');
28004         }
28005         
28006                 
28007     },
28008     onTabClick : function(ev,un,ob,pane)
28009     {
28010         //Roo.log('tab - prev default');
28011         ev.preventDefault();
28012         
28013         
28014         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28015         pane.tab.addClass('active');
28016         //Roo.log(pane.title);
28017         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28018         // technically we should have a deactivate event.. but maybe add later.
28019         // and it should not de-activate the selected tab...
28020         this.fireEvent('activatepane', pane);
28021         pane.el.addClass('active');
28022         pane.fireEvent('activate');
28023         
28024         
28025     },
28026     
28027     getActivePane : function()
28028     {
28029         var r = false;
28030         Roo.each(this.panes, function(p) {
28031             if(p.el.hasClass('active')){
28032                 r = p;
28033                 return false;
28034             }
28035             
28036             return;
28037         });
28038         
28039         return r;
28040     }
28041     
28042     
28043 });
28044
28045  
28046 /*
28047  * - LGPL
28048  *
28049  * Tab pane
28050  * 
28051  */
28052 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28053 /**
28054  * @class Roo.bootstrap.TabPane
28055  * @extends Roo.bootstrap.Component
28056  * Bootstrap TabPane class
28057  * @cfg {Boolean} active (false | true) Default false
28058  * @cfg {String} title title of panel
28059
28060  * 
28061  * @constructor
28062  * Create a new TabPane
28063  * @param {Object} config The config object
28064  */
28065
28066 Roo.bootstrap.dash.TabPane = function(config){
28067     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28068     
28069     this.addEvents({
28070         // raw events
28071         /**
28072          * @event activate
28073          * When a pane is activated
28074          * @param {Roo.bootstrap.dash.TabPane} pane
28075          */
28076         "activate" : true
28077          
28078     });
28079 };
28080
28081 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28082     
28083     active : false,
28084     title : '',
28085     
28086     // the tabBox that this is attached to.
28087     tab : false,
28088      
28089     getAutoCreate : function() 
28090     {
28091         var cfg = {
28092             tag: 'div',
28093             cls: 'tab-pane'
28094         };
28095         
28096         if(this.active){
28097             cfg.cls += ' active';
28098         }
28099         
28100         return cfg;
28101     },
28102     initEvents  : function()
28103     {
28104         //Roo.log('trigger add pane handler');
28105         this.parent().fireEvent('addpane', this)
28106     },
28107     
28108      /**
28109      * Updates the tab title 
28110      * @param {String} html to set the title to.
28111      */
28112     setTitle: function(str)
28113     {
28114         if (!this.tab) {
28115             return;
28116         }
28117         this.title = str;
28118         this.tab.select('a', true).first().dom.innerHTML = str;
28119         
28120     }
28121     
28122     
28123     
28124 });
28125
28126  
28127
28128
28129  /*
28130  * - LGPL
28131  *
28132  * menu
28133  * 
28134  */
28135 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28136
28137 /**
28138  * @class Roo.bootstrap.menu.Menu
28139  * @extends Roo.bootstrap.Component
28140  * Bootstrap Menu class - container for Menu
28141  * @cfg {String} html Text of the menu
28142  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28143  * @cfg {String} icon Font awesome icon
28144  * @cfg {String} pos Menu align to (top | bottom) default bottom
28145  * 
28146  * 
28147  * @constructor
28148  * Create a new Menu
28149  * @param {Object} config The config object
28150  */
28151
28152
28153 Roo.bootstrap.menu.Menu = function(config){
28154     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28155     
28156     this.addEvents({
28157         /**
28158          * @event beforeshow
28159          * Fires before this menu is displayed
28160          * @param {Roo.bootstrap.menu.Menu} this
28161          */
28162         beforeshow : true,
28163         /**
28164          * @event beforehide
28165          * Fires before this menu is hidden
28166          * @param {Roo.bootstrap.menu.Menu} this
28167          */
28168         beforehide : true,
28169         /**
28170          * @event show
28171          * Fires after this menu is displayed
28172          * @param {Roo.bootstrap.menu.Menu} this
28173          */
28174         show : true,
28175         /**
28176          * @event hide
28177          * Fires after this menu is hidden
28178          * @param {Roo.bootstrap.menu.Menu} this
28179          */
28180         hide : true,
28181         /**
28182          * @event click
28183          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28184          * @param {Roo.bootstrap.menu.Menu} this
28185          * @param {Roo.EventObject} e
28186          */
28187         click : true
28188     });
28189     
28190 };
28191
28192 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28193     
28194     submenu : false,
28195     html : '',
28196     weight : 'default',
28197     icon : false,
28198     pos : 'bottom',
28199     
28200     
28201     getChildContainer : function() {
28202         if(this.isSubMenu){
28203             return this.el;
28204         }
28205         
28206         return this.el.select('ul.dropdown-menu', true).first();  
28207     },
28208     
28209     getAutoCreate : function()
28210     {
28211         var text = [
28212             {
28213                 tag : 'span',
28214                 cls : 'roo-menu-text',
28215                 html : this.html
28216             }
28217         ];
28218         
28219         if(this.icon){
28220             text.unshift({
28221                 tag : 'i',
28222                 cls : 'fa ' + this.icon
28223             })
28224         }
28225         
28226         
28227         var cfg = {
28228             tag : 'div',
28229             cls : 'btn-group',
28230             cn : [
28231                 {
28232                     tag : 'button',
28233                     cls : 'dropdown-button btn btn-' + this.weight,
28234                     cn : text
28235                 },
28236                 {
28237                     tag : 'button',
28238                     cls : 'dropdown-toggle btn btn-' + this.weight,
28239                     cn : [
28240                         {
28241                             tag : 'span',
28242                             cls : 'caret'
28243                         }
28244                     ]
28245                 },
28246                 {
28247                     tag : 'ul',
28248                     cls : 'dropdown-menu'
28249                 }
28250             ]
28251             
28252         };
28253         
28254         if(this.pos == 'top'){
28255             cfg.cls += ' dropup';
28256         }
28257         
28258         if(this.isSubMenu){
28259             cfg = {
28260                 tag : 'ul',
28261                 cls : 'dropdown-menu'
28262             }
28263         }
28264         
28265         return cfg;
28266     },
28267     
28268     onRender : function(ct, position)
28269     {
28270         this.isSubMenu = ct.hasClass('dropdown-submenu');
28271         
28272         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28273     },
28274     
28275     initEvents : function() 
28276     {
28277         if(this.isSubMenu){
28278             return;
28279         }
28280         
28281         this.hidden = true;
28282         
28283         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28284         this.triggerEl.on('click', this.onTriggerPress, this);
28285         
28286         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28287         this.buttonEl.on('click', this.onClick, this);
28288         
28289     },
28290     
28291     list : function()
28292     {
28293         if(this.isSubMenu){
28294             return this.el;
28295         }
28296         
28297         return this.el.select('ul.dropdown-menu', true).first();
28298     },
28299     
28300     onClick : function(e)
28301     {
28302         this.fireEvent("click", this, e);
28303     },
28304     
28305     onTriggerPress  : function(e)
28306     {   
28307         if (this.isVisible()) {
28308             this.hide();
28309         } else {
28310             this.show();
28311         }
28312     },
28313     
28314     isVisible : function(){
28315         return !this.hidden;
28316     },
28317     
28318     show : function()
28319     {
28320         this.fireEvent("beforeshow", this);
28321         
28322         this.hidden = false;
28323         this.el.addClass('open');
28324         
28325         Roo.get(document).on("mouseup", this.onMouseUp, this);
28326         
28327         this.fireEvent("show", this);
28328         
28329         
28330     },
28331     
28332     hide : function()
28333     {
28334         this.fireEvent("beforehide", this);
28335         
28336         this.hidden = true;
28337         this.el.removeClass('open');
28338         
28339         Roo.get(document).un("mouseup", this.onMouseUp);
28340         
28341         this.fireEvent("hide", this);
28342     },
28343     
28344     onMouseUp : function()
28345     {
28346         this.hide();
28347     }
28348     
28349 });
28350
28351  
28352  /*
28353  * - LGPL
28354  *
28355  * menu item
28356  * 
28357  */
28358 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28359
28360 /**
28361  * @class Roo.bootstrap.menu.Item
28362  * @extends Roo.bootstrap.Component
28363  * Bootstrap MenuItem class
28364  * @cfg {Boolean} submenu (true | false) default false
28365  * @cfg {String} html text of the item
28366  * @cfg {String} href the link
28367  * @cfg {Boolean} disable (true | false) default false
28368  * @cfg {Boolean} preventDefault (true | false) default true
28369  * @cfg {String} icon Font awesome icon
28370  * @cfg {String} pos Submenu align to (left | right) default right 
28371  * 
28372  * 
28373  * @constructor
28374  * Create a new Item
28375  * @param {Object} config The config object
28376  */
28377
28378
28379 Roo.bootstrap.menu.Item = function(config){
28380     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28381     this.addEvents({
28382         /**
28383          * @event mouseover
28384          * Fires when the mouse is hovering over this menu
28385          * @param {Roo.bootstrap.menu.Item} this
28386          * @param {Roo.EventObject} e
28387          */
28388         mouseover : true,
28389         /**
28390          * @event mouseout
28391          * Fires when the mouse exits this menu
28392          * @param {Roo.bootstrap.menu.Item} this
28393          * @param {Roo.EventObject} e
28394          */
28395         mouseout : true,
28396         // raw events
28397         /**
28398          * @event click
28399          * The raw click event for the entire grid.
28400          * @param {Roo.EventObject} e
28401          */
28402         click : true
28403     });
28404 };
28405
28406 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28407     
28408     submenu : false,
28409     href : '',
28410     html : '',
28411     preventDefault: true,
28412     disable : false,
28413     icon : false,
28414     pos : 'right',
28415     
28416     getAutoCreate : function()
28417     {
28418         var text = [
28419             {
28420                 tag : 'span',
28421                 cls : 'roo-menu-item-text',
28422                 html : this.html
28423             }
28424         ];
28425         
28426         if(this.icon){
28427             text.unshift({
28428                 tag : 'i',
28429                 cls : 'fa ' + this.icon
28430             })
28431         }
28432         
28433         var cfg = {
28434             tag : 'li',
28435             cn : [
28436                 {
28437                     tag : 'a',
28438                     href : this.href || '#',
28439                     cn : text
28440                 }
28441             ]
28442         };
28443         
28444         if(this.disable){
28445             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28446         }
28447         
28448         if(this.submenu){
28449             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28450             
28451             if(this.pos == 'left'){
28452                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28453             }
28454         }
28455         
28456         return cfg;
28457     },
28458     
28459     initEvents : function() 
28460     {
28461         this.el.on('mouseover', this.onMouseOver, this);
28462         this.el.on('mouseout', this.onMouseOut, this);
28463         
28464         this.el.select('a', true).first().on('click', this.onClick, this);
28465         
28466     },
28467     
28468     onClick : function(e)
28469     {
28470         if(this.preventDefault){
28471             e.preventDefault();
28472         }
28473         
28474         this.fireEvent("click", this, e);
28475     },
28476     
28477     onMouseOver : function(e)
28478     {
28479         if(this.submenu && this.pos == 'left'){
28480             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28481         }
28482         
28483         this.fireEvent("mouseover", this, e);
28484     },
28485     
28486     onMouseOut : function(e)
28487     {
28488         this.fireEvent("mouseout", this, e);
28489     }
28490 });
28491
28492  
28493
28494  /*
28495  * - LGPL
28496  *
28497  * menu separator
28498  * 
28499  */
28500 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28501
28502 /**
28503  * @class Roo.bootstrap.menu.Separator
28504  * @extends Roo.bootstrap.Component
28505  * Bootstrap Separator class
28506  * 
28507  * @constructor
28508  * Create a new Separator
28509  * @param {Object} config The config object
28510  */
28511
28512
28513 Roo.bootstrap.menu.Separator = function(config){
28514     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28515 };
28516
28517 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28518     
28519     getAutoCreate : function(){
28520         var cfg = {
28521             tag : 'li',
28522             cls: 'divider'
28523         };
28524         
28525         return cfg;
28526     }
28527    
28528 });
28529
28530  
28531
28532  /*
28533  * - LGPL
28534  *
28535  * Tooltip
28536  * 
28537  */
28538
28539 /**
28540  * @class Roo.bootstrap.Tooltip
28541  * Bootstrap Tooltip class
28542  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28543  * to determine which dom element triggers the tooltip.
28544  * 
28545  * It needs to add support for additional attributes like tooltip-position
28546  * 
28547  * @constructor
28548  * Create a new Toolti
28549  * @param {Object} config The config object
28550  */
28551
28552 Roo.bootstrap.Tooltip = function(config){
28553     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28554     
28555     this.alignment = Roo.bootstrap.Tooltip.alignment;
28556     
28557     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28558         this.alignment = config.alignment;
28559     }
28560     
28561 };
28562
28563 Roo.apply(Roo.bootstrap.Tooltip, {
28564     /**
28565      * @function init initialize tooltip monitoring.
28566      * @static
28567      */
28568     currentEl : false,
28569     currentTip : false,
28570     currentRegion : false,
28571     
28572     //  init : delay?
28573     
28574     init : function()
28575     {
28576         Roo.get(document).on('mouseover', this.enter ,this);
28577         Roo.get(document).on('mouseout', this.leave, this);
28578          
28579         
28580         this.currentTip = new Roo.bootstrap.Tooltip();
28581     },
28582     
28583     enter : function(ev)
28584     {
28585         var dom = ev.getTarget();
28586         
28587         //Roo.log(['enter',dom]);
28588         var el = Roo.fly(dom);
28589         if (this.currentEl) {
28590             //Roo.log(dom);
28591             //Roo.log(this.currentEl);
28592             //Roo.log(this.currentEl.contains(dom));
28593             if (this.currentEl == el) {
28594                 return;
28595             }
28596             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28597                 return;
28598             }
28599
28600         }
28601         
28602         if (this.currentTip.el) {
28603             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28604         }    
28605         //Roo.log(ev);
28606         
28607         if(!el || el.dom == document){
28608             return;
28609         }
28610         
28611         var bindEl = el;
28612         
28613         // you can not look for children, as if el is the body.. then everythign is the child..
28614         if (!el.attr('tooltip')) { //
28615             if (!el.select("[tooltip]").elements.length) {
28616                 return;
28617             }
28618             // is the mouse over this child...?
28619             bindEl = el.select("[tooltip]").first();
28620             var xy = ev.getXY();
28621             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28622                 //Roo.log("not in region.");
28623                 return;
28624             }
28625             //Roo.log("child element over..");
28626             
28627         }
28628         this.currentEl = bindEl;
28629         this.currentTip.bind(bindEl);
28630         this.currentRegion = Roo.lib.Region.getRegion(dom);
28631         this.currentTip.enter();
28632         
28633     },
28634     leave : function(ev)
28635     {
28636         var dom = ev.getTarget();
28637         //Roo.log(['leave',dom]);
28638         if (!this.currentEl) {
28639             return;
28640         }
28641         
28642         
28643         if (dom != this.currentEl.dom) {
28644             return;
28645         }
28646         var xy = ev.getXY();
28647         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28648             return;
28649         }
28650         // only activate leave if mouse cursor is outside... bounding box..
28651         
28652         
28653         
28654         
28655         if (this.currentTip) {
28656             this.currentTip.leave();
28657         }
28658         //Roo.log('clear currentEl');
28659         this.currentEl = false;
28660         
28661         
28662     },
28663     alignment : {
28664         'left' : ['r-l', [-2,0], 'right'],
28665         'right' : ['l-r', [2,0], 'left'],
28666         'bottom' : ['t-b', [0,2], 'top'],
28667         'top' : [ 'b-t', [0,-2], 'bottom']
28668     }
28669     
28670 });
28671
28672
28673 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28674     
28675     
28676     bindEl : false,
28677     
28678     delay : null, // can be { show : 300 , hide: 500}
28679     
28680     timeout : null,
28681     
28682     hoverState : null, //???
28683     
28684     placement : 'bottom', 
28685     
28686     alignment : false,
28687     
28688     getAutoCreate : function(){
28689     
28690         var cfg = {
28691            cls : 'tooltip',   
28692            role : 'tooltip',
28693            cn : [
28694                 {
28695                     cls : 'tooltip-arrow arrow'
28696                 },
28697                 {
28698                     cls : 'tooltip-inner'
28699                 }
28700            ]
28701         };
28702         
28703         return cfg;
28704     },
28705     bind : function(el)
28706     {
28707         this.bindEl = el;
28708     },
28709     
28710     initEvents : function()
28711     {
28712         this.arrowEl = this.el.select('.arrow', true).first();
28713         this.innerEl = this.el.select('.tooltip-inner', true).first();
28714     },
28715     
28716     enter : function () {
28717        
28718         if (this.timeout != null) {
28719             clearTimeout(this.timeout);
28720         }
28721         
28722         this.hoverState = 'in';
28723          //Roo.log("enter - show");
28724         if (!this.delay || !this.delay.show) {
28725             this.show();
28726             return;
28727         }
28728         var _t = this;
28729         this.timeout = setTimeout(function () {
28730             if (_t.hoverState == 'in') {
28731                 _t.show();
28732             }
28733         }, this.delay.show);
28734     },
28735     leave : function()
28736     {
28737         clearTimeout(this.timeout);
28738     
28739         this.hoverState = 'out';
28740          if (!this.delay || !this.delay.hide) {
28741             this.hide();
28742             return;
28743         }
28744        
28745         var _t = this;
28746         this.timeout = setTimeout(function () {
28747             //Roo.log("leave - timeout");
28748             
28749             if (_t.hoverState == 'out') {
28750                 _t.hide();
28751                 Roo.bootstrap.Tooltip.currentEl = false;
28752             }
28753         }, delay);
28754     },
28755     
28756     show : function (msg)
28757     {
28758         if (!this.el) {
28759             this.render(document.body);
28760         }
28761         // set content.
28762         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28763         
28764         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28765         
28766         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28767         
28768         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28769                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28770         
28771         var placement = typeof this.placement == 'function' ?
28772             this.placement.call(this, this.el, on_el) :
28773             this.placement;
28774             
28775         var autoToken = /\s?auto?\s?/i;
28776         var autoPlace = autoToken.test(placement);
28777         if (autoPlace) {
28778             placement = placement.replace(autoToken, '') || 'top';
28779         }
28780         
28781         //this.el.detach()
28782         //this.el.setXY([0,0]);
28783         this.el.show();
28784         //this.el.dom.style.display='block';
28785         
28786         //this.el.appendTo(on_el);
28787         
28788         var p = this.getPosition();
28789         var box = this.el.getBox();
28790         
28791         if (autoPlace) {
28792             // fixme..
28793         }
28794         
28795         var align = this.alignment[placement];
28796         
28797         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28798         
28799         if(placement == 'top' || placement == 'bottom'){
28800             if(xy[0] < 0){
28801                 placement = 'right';
28802             }
28803             
28804             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28805                 placement = 'left';
28806             }
28807             
28808             var scroll = Roo.select('body', true).first().getScroll();
28809             
28810             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28811                 placement = 'top';
28812             }
28813             
28814             align = this.alignment[placement];
28815             
28816             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28817             
28818         }
28819         
28820         this.el.alignTo(this.bindEl, align[0],align[1]);
28821         //var arrow = this.el.select('.arrow',true).first();
28822         //arrow.set(align[2], 
28823         
28824         this.el.addClass(placement);
28825         this.el.addClass("bs-tooltip-"+ placement);
28826         
28827         this.el.addClass('in fade show');
28828         
28829         this.hoverState = null;
28830         
28831         if (this.el.hasClass('fade')) {
28832             // fade it?
28833         }
28834         
28835         
28836         
28837         
28838         
28839     },
28840     hide : function()
28841     {
28842          
28843         if (!this.el) {
28844             return;
28845         }
28846         //this.el.setXY([0,0]);
28847         this.el.removeClass(['show', 'in']);
28848         //this.el.hide();
28849         
28850     }
28851     
28852 });
28853  
28854
28855  /*
28856  * - LGPL
28857  *
28858  * Location Picker
28859  * 
28860  */
28861
28862 /**
28863  * @class Roo.bootstrap.LocationPicker
28864  * @extends Roo.bootstrap.Component
28865  * Bootstrap LocationPicker class
28866  * @cfg {Number} latitude Position when init default 0
28867  * @cfg {Number} longitude Position when init default 0
28868  * @cfg {Number} zoom default 15
28869  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28870  * @cfg {Boolean} mapTypeControl default false
28871  * @cfg {Boolean} disableDoubleClickZoom default false
28872  * @cfg {Boolean} scrollwheel default true
28873  * @cfg {Boolean} streetViewControl default false
28874  * @cfg {Number} radius default 0
28875  * @cfg {String} locationName
28876  * @cfg {Boolean} draggable default true
28877  * @cfg {Boolean} enableAutocomplete default false
28878  * @cfg {Boolean} enableReverseGeocode default true
28879  * @cfg {String} markerTitle
28880  * 
28881  * @constructor
28882  * Create a new LocationPicker
28883  * @param {Object} config The config object
28884  */
28885
28886
28887 Roo.bootstrap.LocationPicker = function(config){
28888     
28889     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28890     
28891     this.addEvents({
28892         /**
28893          * @event initial
28894          * Fires when the picker initialized.
28895          * @param {Roo.bootstrap.LocationPicker} this
28896          * @param {Google Location} location
28897          */
28898         initial : true,
28899         /**
28900          * @event positionchanged
28901          * Fires when the picker position changed.
28902          * @param {Roo.bootstrap.LocationPicker} this
28903          * @param {Google Location} location
28904          */
28905         positionchanged : true,
28906         /**
28907          * @event resize
28908          * Fires when the map resize.
28909          * @param {Roo.bootstrap.LocationPicker} this
28910          */
28911         resize : true,
28912         /**
28913          * @event show
28914          * Fires when the map show.
28915          * @param {Roo.bootstrap.LocationPicker} this
28916          */
28917         show : true,
28918         /**
28919          * @event hide
28920          * Fires when the map hide.
28921          * @param {Roo.bootstrap.LocationPicker} this
28922          */
28923         hide : true,
28924         /**
28925          * @event mapClick
28926          * Fires when click the map.
28927          * @param {Roo.bootstrap.LocationPicker} this
28928          * @param {Map event} e
28929          */
28930         mapClick : true,
28931         /**
28932          * @event mapRightClick
28933          * Fires when right click the map.
28934          * @param {Roo.bootstrap.LocationPicker} this
28935          * @param {Map event} e
28936          */
28937         mapRightClick : true,
28938         /**
28939          * @event markerClick
28940          * Fires when click the marker.
28941          * @param {Roo.bootstrap.LocationPicker} this
28942          * @param {Map event} e
28943          */
28944         markerClick : true,
28945         /**
28946          * @event markerRightClick
28947          * Fires when right click the marker.
28948          * @param {Roo.bootstrap.LocationPicker} this
28949          * @param {Map event} e
28950          */
28951         markerRightClick : true,
28952         /**
28953          * @event OverlayViewDraw
28954          * Fires when OverlayView Draw
28955          * @param {Roo.bootstrap.LocationPicker} this
28956          */
28957         OverlayViewDraw : true,
28958         /**
28959          * @event OverlayViewOnAdd
28960          * Fires when OverlayView Draw
28961          * @param {Roo.bootstrap.LocationPicker} this
28962          */
28963         OverlayViewOnAdd : true,
28964         /**
28965          * @event OverlayViewOnRemove
28966          * Fires when OverlayView Draw
28967          * @param {Roo.bootstrap.LocationPicker} this
28968          */
28969         OverlayViewOnRemove : true,
28970         /**
28971          * @event OverlayViewShow
28972          * Fires when OverlayView Draw
28973          * @param {Roo.bootstrap.LocationPicker} this
28974          * @param {Pixel} cpx
28975          */
28976         OverlayViewShow : true,
28977         /**
28978          * @event OverlayViewHide
28979          * Fires when OverlayView Draw
28980          * @param {Roo.bootstrap.LocationPicker} this
28981          */
28982         OverlayViewHide : true,
28983         /**
28984          * @event loadexception
28985          * Fires when load google lib failed.
28986          * @param {Roo.bootstrap.LocationPicker} this
28987          */
28988         loadexception : true
28989     });
28990         
28991 };
28992
28993 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
28994     
28995     gMapContext: false,
28996     
28997     latitude: 0,
28998     longitude: 0,
28999     zoom: 15,
29000     mapTypeId: false,
29001     mapTypeControl: false,
29002     disableDoubleClickZoom: false,
29003     scrollwheel: true,
29004     streetViewControl: false,
29005     radius: 0,
29006     locationName: '',
29007     draggable: true,
29008     enableAutocomplete: false,
29009     enableReverseGeocode: true,
29010     markerTitle: '',
29011     
29012     getAutoCreate: function()
29013     {
29014
29015         var cfg = {
29016             tag: 'div',
29017             cls: 'roo-location-picker'
29018         };
29019         
29020         return cfg
29021     },
29022     
29023     initEvents: function(ct, position)
29024     {       
29025         if(!this.el.getWidth() || this.isApplied()){
29026             return;
29027         }
29028         
29029         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29030         
29031         this.initial();
29032     },
29033     
29034     initial: function()
29035     {
29036         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29037             this.fireEvent('loadexception', this);
29038             return;
29039         }
29040         
29041         if(!this.mapTypeId){
29042             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29043         }
29044         
29045         this.gMapContext = this.GMapContext();
29046         
29047         this.initOverlayView();
29048         
29049         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29050         
29051         var _this = this;
29052                 
29053         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29054             _this.setPosition(_this.gMapContext.marker.position);
29055         });
29056         
29057         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29058             _this.fireEvent('mapClick', this, event);
29059             
29060         });
29061
29062         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29063             _this.fireEvent('mapRightClick', this, event);
29064             
29065         });
29066         
29067         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29068             _this.fireEvent('markerClick', this, event);
29069             
29070         });
29071
29072         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29073             _this.fireEvent('markerRightClick', this, event);
29074             
29075         });
29076         
29077         this.setPosition(this.gMapContext.location);
29078         
29079         this.fireEvent('initial', this, this.gMapContext.location);
29080     },
29081     
29082     initOverlayView: function()
29083     {
29084         var _this = this;
29085         
29086         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29087             
29088             draw: function()
29089             {
29090                 _this.fireEvent('OverlayViewDraw', _this);
29091             },
29092             
29093             onAdd: function()
29094             {
29095                 _this.fireEvent('OverlayViewOnAdd', _this);
29096             },
29097             
29098             onRemove: function()
29099             {
29100                 _this.fireEvent('OverlayViewOnRemove', _this);
29101             },
29102             
29103             show: function(cpx)
29104             {
29105                 _this.fireEvent('OverlayViewShow', _this, cpx);
29106             },
29107             
29108             hide: function()
29109             {
29110                 _this.fireEvent('OverlayViewHide', _this);
29111             }
29112             
29113         });
29114     },
29115     
29116     fromLatLngToContainerPixel: function(event)
29117     {
29118         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29119     },
29120     
29121     isApplied: function() 
29122     {
29123         return this.getGmapContext() == false ? false : true;
29124     },
29125     
29126     getGmapContext: function() 
29127     {
29128         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29129     },
29130     
29131     GMapContext: function() 
29132     {
29133         var position = new google.maps.LatLng(this.latitude, this.longitude);
29134         
29135         var _map = new google.maps.Map(this.el.dom, {
29136             center: position,
29137             zoom: this.zoom,
29138             mapTypeId: this.mapTypeId,
29139             mapTypeControl: this.mapTypeControl,
29140             disableDoubleClickZoom: this.disableDoubleClickZoom,
29141             scrollwheel: this.scrollwheel,
29142             streetViewControl: this.streetViewControl,
29143             locationName: this.locationName,
29144             draggable: this.draggable,
29145             enableAutocomplete: this.enableAutocomplete,
29146             enableReverseGeocode: this.enableReverseGeocode
29147         });
29148         
29149         var _marker = new google.maps.Marker({
29150             position: position,
29151             map: _map,
29152             title: this.markerTitle,
29153             draggable: this.draggable
29154         });
29155         
29156         return {
29157             map: _map,
29158             marker: _marker,
29159             circle: null,
29160             location: position,
29161             radius: this.radius,
29162             locationName: this.locationName,
29163             addressComponents: {
29164                 formatted_address: null,
29165                 addressLine1: null,
29166                 addressLine2: null,
29167                 streetName: null,
29168                 streetNumber: null,
29169                 city: null,
29170                 district: null,
29171                 state: null,
29172                 stateOrProvince: null
29173             },
29174             settings: this,
29175             domContainer: this.el.dom,
29176             geodecoder: new google.maps.Geocoder()
29177         };
29178     },
29179     
29180     drawCircle: function(center, radius, options) 
29181     {
29182         if (this.gMapContext.circle != null) {
29183             this.gMapContext.circle.setMap(null);
29184         }
29185         if (radius > 0) {
29186             radius *= 1;
29187             options = Roo.apply({}, options, {
29188                 strokeColor: "#0000FF",
29189                 strokeOpacity: .35,
29190                 strokeWeight: 2,
29191                 fillColor: "#0000FF",
29192                 fillOpacity: .2
29193             });
29194             
29195             options.map = this.gMapContext.map;
29196             options.radius = radius;
29197             options.center = center;
29198             this.gMapContext.circle = new google.maps.Circle(options);
29199             return this.gMapContext.circle;
29200         }
29201         
29202         return null;
29203     },
29204     
29205     setPosition: function(location) 
29206     {
29207         this.gMapContext.location = location;
29208         this.gMapContext.marker.setPosition(location);
29209         this.gMapContext.map.panTo(location);
29210         this.drawCircle(location, this.gMapContext.radius, {});
29211         
29212         var _this = this;
29213         
29214         if (this.gMapContext.settings.enableReverseGeocode) {
29215             this.gMapContext.geodecoder.geocode({
29216                 latLng: this.gMapContext.location
29217             }, function(results, status) {
29218                 
29219                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29220                     _this.gMapContext.locationName = results[0].formatted_address;
29221                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29222                     
29223                     _this.fireEvent('positionchanged', this, location);
29224                 }
29225             });
29226             
29227             return;
29228         }
29229         
29230         this.fireEvent('positionchanged', this, location);
29231     },
29232     
29233     resize: function()
29234     {
29235         google.maps.event.trigger(this.gMapContext.map, "resize");
29236         
29237         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29238         
29239         this.fireEvent('resize', this);
29240     },
29241     
29242     setPositionByLatLng: function(latitude, longitude)
29243     {
29244         this.setPosition(new google.maps.LatLng(latitude, longitude));
29245     },
29246     
29247     getCurrentPosition: function() 
29248     {
29249         return {
29250             latitude: this.gMapContext.location.lat(),
29251             longitude: this.gMapContext.location.lng()
29252         };
29253     },
29254     
29255     getAddressName: function() 
29256     {
29257         return this.gMapContext.locationName;
29258     },
29259     
29260     getAddressComponents: function() 
29261     {
29262         return this.gMapContext.addressComponents;
29263     },
29264     
29265     address_component_from_google_geocode: function(address_components) 
29266     {
29267         var result = {};
29268         
29269         for (var i = 0; i < address_components.length; i++) {
29270             var component = address_components[i];
29271             if (component.types.indexOf("postal_code") >= 0) {
29272                 result.postalCode = component.short_name;
29273             } else if (component.types.indexOf("street_number") >= 0) {
29274                 result.streetNumber = component.short_name;
29275             } else if (component.types.indexOf("route") >= 0) {
29276                 result.streetName = component.short_name;
29277             } else if (component.types.indexOf("neighborhood") >= 0) {
29278                 result.city = component.short_name;
29279             } else if (component.types.indexOf("locality") >= 0) {
29280                 result.city = component.short_name;
29281             } else if (component.types.indexOf("sublocality") >= 0) {
29282                 result.district = component.short_name;
29283             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29284                 result.stateOrProvince = component.short_name;
29285             } else if (component.types.indexOf("country") >= 0) {
29286                 result.country = component.short_name;
29287             }
29288         }
29289         
29290         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29291         result.addressLine2 = "";
29292         return result;
29293     },
29294     
29295     setZoomLevel: function(zoom)
29296     {
29297         this.gMapContext.map.setZoom(zoom);
29298     },
29299     
29300     show: function()
29301     {
29302         if(!this.el){
29303             return;
29304         }
29305         
29306         this.el.show();
29307         
29308         this.resize();
29309         
29310         this.fireEvent('show', this);
29311     },
29312     
29313     hide: function()
29314     {
29315         if(!this.el){
29316             return;
29317         }
29318         
29319         this.el.hide();
29320         
29321         this.fireEvent('hide', this);
29322     }
29323     
29324 });
29325
29326 Roo.apply(Roo.bootstrap.LocationPicker, {
29327     
29328     OverlayView : function(map, options)
29329     {
29330         options = options || {};
29331         
29332         this.setMap(map);
29333     }
29334     
29335     
29336 });/**
29337  * @class Roo.bootstrap.Alert
29338  * @extends Roo.bootstrap.Component
29339  * Bootstrap Alert class - shows an alert area box
29340  * eg
29341  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29342   Enter a valid email address
29343 </div>
29344  * @licence LGPL
29345  * @cfg {String} title The title of alert
29346  * @cfg {String} html The content of alert
29347  * @cfg {String} weight (  success | info | warning | danger )
29348  * @cfg {String} faicon font-awesomeicon
29349  * 
29350  * @constructor
29351  * Create a new alert
29352  * @param {Object} config The config object
29353  */
29354
29355
29356 Roo.bootstrap.Alert = function(config){
29357     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29358     
29359 };
29360
29361 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29362     
29363     title: '',
29364     html: '',
29365     weight: false,
29366     faicon: false,
29367     
29368     getAutoCreate : function()
29369     {
29370         
29371         var cfg = {
29372             tag : 'div',
29373             cls : 'alert',
29374             cn : [
29375                 {
29376                     tag : 'i',
29377                     cls : 'roo-alert-icon'
29378                     
29379                 },
29380                 {
29381                     tag : 'b',
29382                     cls : 'roo-alert-title',
29383                     html : this.title
29384                 },
29385                 {
29386                     tag : 'span',
29387                     cls : 'roo-alert-text',
29388                     html : this.html
29389                 }
29390             ]
29391         };
29392         
29393         if(this.faicon){
29394             cfg.cn[0].cls += ' fa ' + this.faicon;
29395         }
29396         
29397         if(this.weight){
29398             cfg.cls += ' alert-' + this.weight;
29399         }
29400         
29401         return cfg;
29402     },
29403     
29404     initEvents: function() 
29405     {
29406         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29407     },
29408     
29409     setTitle : function(str)
29410     {
29411         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29412     },
29413     
29414     setText : function(str)
29415     {
29416         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29417     },
29418     
29419     setWeight : function(weight)
29420     {
29421         if(this.weight){
29422             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29423         }
29424         
29425         this.weight = weight;
29426         
29427         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29428     },
29429     
29430     setIcon : function(icon)
29431     {
29432         if(this.faicon){
29433             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29434         }
29435         
29436         this.faicon = icon;
29437         
29438         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29439     },
29440     
29441     hide: function() 
29442     {
29443         this.el.hide();   
29444     },
29445     
29446     show: function() 
29447     {  
29448         this.el.show();   
29449     }
29450     
29451 });
29452
29453  
29454 /*
29455 * Licence: LGPL
29456 */
29457
29458 /**
29459  * @class Roo.bootstrap.UploadCropbox
29460  * @extends Roo.bootstrap.Component
29461  * Bootstrap UploadCropbox class
29462  * @cfg {String} emptyText show when image has been loaded
29463  * @cfg {String} rotateNotify show when image too small to rotate
29464  * @cfg {Number} errorTimeout default 3000
29465  * @cfg {Number} minWidth default 300
29466  * @cfg {Number} minHeight default 300
29467  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29468  * @cfg {Boolean} isDocument (true|false) default false
29469  * @cfg {String} url action url
29470  * @cfg {String} paramName default 'imageUpload'
29471  * @cfg {String} method default POST
29472  * @cfg {Boolean} loadMask (true|false) default true
29473  * @cfg {Boolean} loadingText default 'Loading...'
29474  * 
29475  * @constructor
29476  * Create a new UploadCropbox
29477  * @param {Object} config The config object
29478  */
29479
29480 Roo.bootstrap.UploadCropbox = function(config){
29481     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29482     
29483     this.addEvents({
29484         /**
29485          * @event beforeselectfile
29486          * Fire before select file
29487          * @param {Roo.bootstrap.UploadCropbox} this
29488          */
29489         "beforeselectfile" : true,
29490         /**
29491          * @event initial
29492          * Fire after initEvent
29493          * @param {Roo.bootstrap.UploadCropbox} this
29494          */
29495         "initial" : true,
29496         /**
29497          * @event crop
29498          * Fire after initEvent
29499          * @param {Roo.bootstrap.UploadCropbox} this
29500          * @param {String} data
29501          */
29502         "crop" : true,
29503         /**
29504          * @event prepare
29505          * Fire when preparing the file data
29506          * @param {Roo.bootstrap.UploadCropbox} this
29507          * @param {Object} file
29508          */
29509         "prepare" : true,
29510         /**
29511          * @event exception
29512          * Fire when get exception
29513          * @param {Roo.bootstrap.UploadCropbox} this
29514          * @param {XMLHttpRequest} xhr
29515          */
29516         "exception" : true,
29517         /**
29518          * @event beforeloadcanvas
29519          * Fire before load the canvas
29520          * @param {Roo.bootstrap.UploadCropbox} this
29521          * @param {String} src
29522          */
29523         "beforeloadcanvas" : true,
29524         /**
29525          * @event trash
29526          * Fire when trash image
29527          * @param {Roo.bootstrap.UploadCropbox} this
29528          */
29529         "trash" : true,
29530         /**
29531          * @event download
29532          * Fire when download the image
29533          * @param {Roo.bootstrap.UploadCropbox} this
29534          */
29535         "download" : true,
29536         /**
29537          * @event footerbuttonclick
29538          * Fire when footerbuttonclick
29539          * @param {Roo.bootstrap.UploadCropbox} this
29540          * @param {String} type
29541          */
29542         "footerbuttonclick" : true,
29543         /**
29544          * @event resize
29545          * Fire when resize
29546          * @param {Roo.bootstrap.UploadCropbox} this
29547          */
29548         "resize" : true,
29549         /**
29550          * @event rotate
29551          * Fire when rotate the image
29552          * @param {Roo.bootstrap.UploadCropbox} this
29553          * @param {String} pos
29554          */
29555         "rotate" : true,
29556         /**
29557          * @event inspect
29558          * Fire when inspect the file
29559          * @param {Roo.bootstrap.UploadCropbox} this
29560          * @param {Object} file
29561          */
29562         "inspect" : true,
29563         /**
29564          * @event upload
29565          * Fire when xhr upload the file
29566          * @param {Roo.bootstrap.UploadCropbox} this
29567          * @param {Object} data
29568          */
29569         "upload" : true,
29570         /**
29571          * @event arrange
29572          * Fire when arrange the file data
29573          * @param {Roo.bootstrap.UploadCropbox} this
29574          * @param {Object} formData
29575          */
29576         "arrange" : true
29577     });
29578     
29579     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29580 };
29581
29582 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29583     
29584     emptyText : 'Click to upload image',
29585     rotateNotify : 'Image is too small to rotate',
29586     errorTimeout : 3000,
29587     scale : 0,
29588     baseScale : 1,
29589     rotate : 0,
29590     dragable : false,
29591     pinching : false,
29592     mouseX : 0,
29593     mouseY : 0,
29594     cropData : false,
29595     minWidth : 300,
29596     minHeight : 300,
29597     file : false,
29598     exif : {},
29599     baseRotate : 1,
29600     cropType : 'image/jpeg',
29601     buttons : false,
29602     canvasLoaded : false,
29603     isDocument : false,
29604     method : 'POST',
29605     paramName : 'imageUpload',
29606     loadMask : true,
29607     loadingText : 'Loading...',
29608     maskEl : false,
29609     
29610     getAutoCreate : function()
29611     {
29612         var cfg = {
29613             tag : 'div',
29614             cls : 'roo-upload-cropbox',
29615             cn : [
29616                 {
29617                     tag : 'input',
29618                     cls : 'roo-upload-cropbox-selector',
29619                     type : 'file'
29620                 },
29621                 {
29622                     tag : 'div',
29623                     cls : 'roo-upload-cropbox-body',
29624                     style : 'cursor:pointer',
29625                     cn : [
29626                         {
29627                             tag : 'div',
29628                             cls : 'roo-upload-cropbox-preview'
29629                         },
29630                         {
29631                             tag : 'div',
29632                             cls : 'roo-upload-cropbox-thumb'
29633                         },
29634                         {
29635                             tag : 'div',
29636                             cls : 'roo-upload-cropbox-empty-notify',
29637                             html : this.emptyText
29638                         },
29639                         {
29640                             tag : 'div',
29641                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29642                             html : this.rotateNotify
29643                         }
29644                     ]
29645                 },
29646                 {
29647                     tag : 'div',
29648                     cls : 'roo-upload-cropbox-footer',
29649                     cn : {
29650                         tag : 'div',
29651                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29652                         cn : []
29653                     }
29654                 }
29655             ]
29656         };
29657         
29658         return cfg;
29659     },
29660     
29661     onRender : function(ct, position)
29662     {
29663         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29664         
29665         if (this.buttons.length) {
29666             
29667             Roo.each(this.buttons, function(bb) {
29668                 
29669                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29670                 
29671                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29672                 
29673             }, this);
29674         }
29675         
29676         if(this.loadMask){
29677             this.maskEl = this.el;
29678         }
29679     },
29680     
29681     initEvents : function()
29682     {
29683         this.urlAPI = (window.createObjectURL && window) || 
29684                                 (window.URL && URL.revokeObjectURL && URL) || 
29685                                 (window.webkitURL && webkitURL);
29686                         
29687         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29688         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29689         
29690         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29691         this.selectorEl.hide();
29692         
29693         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29694         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29695         
29696         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29697         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29698         this.thumbEl.hide();
29699         
29700         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29701         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29702         
29703         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29704         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29705         this.errorEl.hide();
29706         
29707         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29708         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29709         this.footerEl.hide();
29710         
29711         this.setThumbBoxSize();
29712         
29713         this.bind();
29714         
29715         this.resize();
29716         
29717         this.fireEvent('initial', this);
29718     },
29719
29720     bind : function()
29721     {
29722         var _this = this;
29723         
29724         window.addEventListener("resize", function() { _this.resize(); } );
29725         
29726         this.bodyEl.on('click', this.beforeSelectFile, this);
29727         
29728         if(Roo.isTouch){
29729             this.bodyEl.on('touchstart', this.onTouchStart, this);
29730             this.bodyEl.on('touchmove', this.onTouchMove, this);
29731             this.bodyEl.on('touchend', this.onTouchEnd, this);
29732         }
29733         
29734         if(!Roo.isTouch){
29735             this.bodyEl.on('mousedown', this.onMouseDown, this);
29736             this.bodyEl.on('mousemove', this.onMouseMove, this);
29737             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29738             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29739             Roo.get(document).on('mouseup', this.onMouseUp, this);
29740         }
29741         
29742         this.selectorEl.on('change', this.onFileSelected, this);
29743     },
29744     
29745     reset : function()
29746     {    
29747         this.scale = 0;
29748         this.baseScale = 1;
29749         this.rotate = 0;
29750         this.baseRotate = 1;
29751         this.dragable = false;
29752         this.pinching = false;
29753         this.mouseX = 0;
29754         this.mouseY = 0;
29755         this.cropData = false;
29756         this.notifyEl.dom.innerHTML = this.emptyText;
29757         
29758         this.selectorEl.dom.value = '';
29759         
29760     },
29761     
29762     resize : function()
29763     {
29764         if(this.fireEvent('resize', this) != false){
29765             this.setThumbBoxPosition();
29766             this.setCanvasPosition();
29767         }
29768     },
29769     
29770     onFooterButtonClick : function(e, el, o, type)
29771     {
29772         switch (type) {
29773             case 'rotate-left' :
29774                 this.onRotateLeft(e);
29775                 break;
29776             case 'rotate-right' :
29777                 this.onRotateRight(e);
29778                 break;
29779             case 'picture' :
29780                 this.beforeSelectFile(e);
29781                 break;
29782             case 'trash' :
29783                 this.trash(e);
29784                 break;
29785             case 'crop' :
29786                 this.crop(e);
29787                 break;
29788             case 'download' :
29789                 this.download(e);
29790                 break;
29791             default :
29792                 break;
29793         }
29794         
29795         this.fireEvent('footerbuttonclick', this, type);
29796     },
29797     
29798     beforeSelectFile : function(e)
29799     {
29800         e.preventDefault();
29801         
29802         if(this.fireEvent('beforeselectfile', this) != false){
29803             this.selectorEl.dom.click();
29804         }
29805     },
29806     
29807     onFileSelected : function(e)
29808     {
29809         e.preventDefault();
29810         
29811         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29812             return;
29813         }
29814         
29815         var file = this.selectorEl.dom.files[0];
29816         
29817         if(this.fireEvent('inspect', this, file) != false){
29818             this.prepare(file);
29819         }
29820         
29821     },
29822     
29823     trash : function(e)
29824     {
29825         this.fireEvent('trash', this);
29826     },
29827     
29828     download : function(e)
29829     {
29830         this.fireEvent('download', this);
29831     },
29832     
29833     loadCanvas : function(src)
29834     {   
29835         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29836             
29837             this.reset();
29838             
29839             this.imageEl = document.createElement('img');
29840             
29841             var _this = this;
29842             
29843             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29844             
29845             this.imageEl.src = src;
29846         }
29847     },
29848     
29849     onLoadCanvas : function()
29850     {   
29851         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29852         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29853         
29854         this.bodyEl.un('click', this.beforeSelectFile, this);
29855         
29856         this.notifyEl.hide();
29857         this.thumbEl.show();
29858         this.footerEl.show();
29859         
29860         this.baseRotateLevel();
29861         
29862         if(this.isDocument){
29863             this.setThumbBoxSize();
29864         }
29865         
29866         this.setThumbBoxPosition();
29867         
29868         this.baseScaleLevel();
29869         
29870         this.draw();
29871         
29872         this.resize();
29873         
29874         this.canvasLoaded = true;
29875         
29876         if(this.loadMask){
29877             this.maskEl.unmask();
29878         }
29879         
29880     },
29881     
29882     setCanvasPosition : function()
29883     {   
29884         if(!this.canvasEl){
29885             return;
29886         }
29887         
29888         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29889         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29890         
29891         this.previewEl.setLeft(pw);
29892         this.previewEl.setTop(ph);
29893         
29894     },
29895     
29896     onMouseDown : function(e)
29897     {   
29898         e.stopEvent();
29899         
29900         this.dragable = true;
29901         this.pinching = false;
29902         
29903         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29904             this.dragable = false;
29905             return;
29906         }
29907         
29908         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29909         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29910         
29911     },
29912     
29913     onMouseMove : function(e)
29914     {   
29915         e.stopEvent();
29916         
29917         if(!this.canvasLoaded){
29918             return;
29919         }
29920         
29921         if (!this.dragable){
29922             return;
29923         }
29924         
29925         var minX = Math.ceil(this.thumbEl.getLeft(true));
29926         var minY = Math.ceil(this.thumbEl.getTop(true));
29927         
29928         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29929         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29930         
29931         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29932         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29933         
29934         x = x - this.mouseX;
29935         y = y - this.mouseY;
29936         
29937         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29938         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29939         
29940         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29941         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29942         
29943         this.previewEl.setLeft(bgX);
29944         this.previewEl.setTop(bgY);
29945         
29946         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29947         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29948     },
29949     
29950     onMouseUp : function(e)
29951     {   
29952         e.stopEvent();
29953         
29954         this.dragable = false;
29955     },
29956     
29957     onMouseWheel : function(e)
29958     {   
29959         e.stopEvent();
29960         
29961         this.startScale = this.scale;
29962         
29963         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29964         
29965         if(!this.zoomable()){
29966             this.scale = this.startScale;
29967             return;
29968         }
29969         
29970         this.draw();
29971         
29972         return;
29973     },
29974     
29975     zoomable : function()
29976     {
29977         var minScale = this.thumbEl.getWidth() / this.minWidth;
29978         
29979         if(this.minWidth < this.minHeight){
29980             minScale = this.thumbEl.getHeight() / this.minHeight;
29981         }
29982         
29983         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29984         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29985         
29986         if(
29987                 this.isDocument &&
29988                 (this.rotate == 0 || this.rotate == 180) && 
29989                 (
29990                     width > this.imageEl.OriginWidth || 
29991                     height > this.imageEl.OriginHeight ||
29992                     (width < this.minWidth && height < this.minHeight)
29993                 )
29994         ){
29995             return false;
29996         }
29997         
29998         if(
29999                 this.isDocument &&
30000                 (this.rotate == 90 || this.rotate == 270) && 
30001                 (
30002                     width > this.imageEl.OriginWidth || 
30003                     height > this.imageEl.OriginHeight ||
30004                     (width < this.minHeight && height < this.minWidth)
30005                 )
30006         ){
30007             return false;
30008         }
30009         
30010         if(
30011                 !this.isDocument &&
30012                 (this.rotate == 0 || this.rotate == 180) && 
30013                 (
30014                     width < this.minWidth || 
30015                     width > this.imageEl.OriginWidth || 
30016                     height < this.minHeight || 
30017                     height > this.imageEl.OriginHeight
30018                 )
30019         ){
30020             return false;
30021         }
30022         
30023         if(
30024                 !this.isDocument &&
30025                 (this.rotate == 90 || this.rotate == 270) && 
30026                 (
30027                     width < this.minHeight || 
30028                     width > this.imageEl.OriginWidth || 
30029                     height < this.minWidth || 
30030                     height > this.imageEl.OriginHeight
30031                 )
30032         ){
30033             return false;
30034         }
30035         
30036         return true;
30037         
30038     },
30039     
30040     onRotateLeft : function(e)
30041     {   
30042         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30043             
30044             var minScale = this.thumbEl.getWidth() / this.minWidth;
30045             
30046             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30047             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30048             
30049             this.startScale = this.scale;
30050             
30051             while (this.getScaleLevel() < minScale){
30052             
30053                 this.scale = this.scale + 1;
30054                 
30055                 if(!this.zoomable()){
30056                     break;
30057                 }
30058                 
30059                 if(
30060                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30061                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30062                 ){
30063                     continue;
30064                 }
30065                 
30066                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30067
30068                 this.draw();
30069                 
30070                 return;
30071             }
30072             
30073             this.scale = this.startScale;
30074             
30075             this.onRotateFail();
30076             
30077             return false;
30078         }
30079         
30080         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30081
30082         if(this.isDocument){
30083             this.setThumbBoxSize();
30084             this.setThumbBoxPosition();
30085             this.setCanvasPosition();
30086         }
30087         
30088         this.draw();
30089         
30090         this.fireEvent('rotate', this, 'left');
30091         
30092     },
30093     
30094     onRotateRight : function(e)
30095     {
30096         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30097             
30098             var minScale = this.thumbEl.getWidth() / this.minWidth;
30099         
30100             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30101             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30102             
30103             this.startScale = this.scale;
30104             
30105             while (this.getScaleLevel() < minScale){
30106             
30107                 this.scale = this.scale + 1;
30108                 
30109                 if(!this.zoomable()){
30110                     break;
30111                 }
30112                 
30113                 if(
30114                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30115                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30116                 ){
30117                     continue;
30118                 }
30119                 
30120                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30121
30122                 this.draw();
30123                 
30124                 return;
30125             }
30126             
30127             this.scale = this.startScale;
30128             
30129             this.onRotateFail();
30130             
30131             return false;
30132         }
30133         
30134         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30135
30136         if(this.isDocument){
30137             this.setThumbBoxSize();
30138             this.setThumbBoxPosition();
30139             this.setCanvasPosition();
30140         }
30141         
30142         this.draw();
30143         
30144         this.fireEvent('rotate', this, 'right');
30145     },
30146     
30147     onRotateFail : function()
30148     {
30149         this.errorEl.show(true);
30150         
30151         var _this = this;
30152         
30153         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30154     },
30155     
30156     draw : function()
30157     {
30158         this.previewEl.dom.innerHTML = '';
30159         
30160         var canvasEl = document.createElement("canvas");
30161         
30162         var contextEl = canvasEl.getContext("2d");
30163         
30164         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30165         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30166         var center = this.imageEl.OriginWidth / 2;
30167         
30168         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30169             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30170             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30171             center = this.imageEl.OriginHeight / 2;
30172         }
30173         
30174         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30175         
30176         contextEl.translate(center, center);
30177         contextEl.rotate(this.rotate * Math.PI / 180);
30178
30179         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30180         
30181         this.canvasEl = document.createElement("canvas");
30182         
30183         this.contextEl = this.canvasEl.getContext("2d");
30184         
30185         switch (this.rotate) {
30186             case 0 :
30187                 
30188                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30189                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30190                 
30191                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30192                 
30193                 break;
30194             case 90 : 
30195                 
30196                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30197                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30198                 
30199                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30200                     this.contextEl.drawImage(canvasEl, Math.abs(this.canvasEl.width - this.canvasEl.height), 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30201                     break;
30202                 }
30203                 
30204                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30205                 
30206                 break;
30207             case 180 :
30208                 
30209                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30210                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30211                 
30212                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30213                     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);
30214                     break;
30215                 }
30216                 
30217                 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);
30218                 
30219                 break;
30220             case 270 :
30221                 
30222                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30223                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30224         
30225                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30226                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30227                     break;
30228                 }
30229                 
30230                 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);
30231                 
30232                 break;
30233             default : 
30234                 break;
30235         }
30236         
30237         this.previewEl.appendChild(this.canvasEl);
30238         
30239         this.setCanvasPosition();
30240     },
30241     
30242     crop : function()
30243     {
30244         if(!this.canvasLoaded){
30245             return;
30246         }
30247         
30248         var imageCanvas = document.createElement("canvas");
30249         
30250         var imageContext = imageCanvas.getContext("2d");
30251         
30252         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30253         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30254         
30255         var center = imageCanvas.width / 2;
30256         
30257         imageContext.translate(center, center);
30258         
30259         imageContext.rotate(this.rotate * Math.PI / 180);
30260         
30261         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30262         
30263         var canvas = document.createElement("canvas");
30264         
30265         var context = canvas.getContext("2d");
30266                 
30267         canvas.width = this.minWidth;
30268         canvas.height = this.minHeight;
30269
30270         switch (this.rotate) {
30271             case 0 :
30272                 
30273                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30274                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30275                 
30276                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30277                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30278                 
30279                 var targetWidth = this.minWidth - 2 * x;
30280                 var targetHeight = this.minHeight - 2 * y;
30281                 
30282                 var scale = 1;
30283                 
30284                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30285                     scale = targetWidth / width;
30286                 }
30287                 
30288                 if(x > 0 && y == 0){
30289                     scale = targetHeight / height;
30290                 }
30291                 
30292                 if(x > 0 && y > 0){
30293                     scale = targetWidth / width;
30294                     
30295                     if(width < height){
30296                         scale = targetHeight / height;
30297                     }
30298                 }
30299                 
30300                 context.scale(scale, scale);
30301                 
30302                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30303                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30304
30305                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30306                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30307
30308                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30309                 
30310                 break;
30311             case 90 : 
30312                 
30313                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30314                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30315                 
30316                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30317                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30318                 
30319                 var targetWidth = this.minWidth - 2 * x;
30320                 var targetHeight = this.minHeight - 2 * y;
30321                 
30322                 var scale = 1;
30323                 
30324                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30325                     scale = targetWidth / width;
30326                 }
30327                 
30328                 if(x > 0 && y == 0){
30329                     scale = targetHeight / height;
30330                 }
30331                 
30332                 if(x > 0 && y > 0){
30333                     scale = targetWidth / width;
30334                     
30335                     if(width < height){
30336                         scale = targetHeight / height;
30337                     }
30338                 }
30339                 
30340                 context.scale(scale, scale);
30341                 
30342                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30343                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30344
30345                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30346                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30347                 
30348                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30349                 
30350                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30351                 
30352                 break;
30353             case 180 :
30354                 
30355                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30356                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30357                 
30358                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30359                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30360                 
30361                 var targetWidth = this.minWidth - 2 * x;
30362                 var targetHeight = this.minHeight - 2 * y;
30363                 
30364                 var scale = 1;
30365                 
30366                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30367                     scale = targetWidth / width;
30368                 }
30369                 
30370                 if(x > 0 && y == 0){
30371                     scale = targetHeight / height;
30372                 }
30373                 
30374                 if(x > 0 && y > 0){
30375                     scale = targetWidth / width;
30376                     
30377                     if(width < height){
30378                         scale = targetHeight / height;
30379                     }
30380                 }
30381                 
30382                 context.scale(scale, scale);
30383                 
30384                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30385                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30386
30387                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30388                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30389
30390                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30391                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30392                 
30393                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30394                 
30395                 break;
30396             case 270 :
30397                 
30398                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30399                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30400                 
30401                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30402                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30403                 
30404                 var targetWidth = this.minWidth - 2 * x;
30405                 var targetHeight = this.minHeight - 2 * y;
30406                 
30407                 var scale = 1;
30408                 
30409                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30410                     scale = targetWidth / width;
30411                 }
30412                 
30413                 if(x > 0 && y == 0){
30414                     scale = targetHeight / height;
30415                 }
30416                 
30417                 if(x > 0 && y > 0){
30418                     scale = targetWidth / width;
30419                     
30420                     if(width < height){
30421                         scale = targetHeight / height;
30422                     }
30423                 }
30424                 
30425                 context.scale(scale, scale);
30426                 
30427                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30428                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30429
30430                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30431                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30432                 
30433                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30434                 
30435                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30436                 
30437                 break;
30438             default : 
30439                 break;
30440         }
30441         
30442         this.cropData = canvas.toDataURL(this.cropType);
30443         
30444         if(this.fireEvent('crop', this, this.cropData) !== false){
30445             this.process(this.file, this.cropData);
30446         }
30447         
30448         return;
30449         
30450     },
30451     
30452     setThumbBoxSize : function()
30453     {
30454         var width, height;
30455         
30456         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30457             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30458             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30459             
30460             this.minWidth = width;
30461             this.minHeight = height;
30462             
30463             if(this.rotate == 90 || this.rotate == 270){
30464                 this.minWidth = height;
30465                 this.minHeight = width;
30466             }
30467         }
30468         
30469         height = 300;
30470         width = Math.ceil(this.minWidth * height / this.minHeight);
30471         
30472         if(this.minWidth > this.minHeight){
30473             width = 300;
30474             height = Math.ceil(this.minHeight * width / this.minWidth);
30475         }
30476         
30477         this.thumbEl.setStyle({
30478             width : width + 'px',
30479             height : height + 'px'
30480         });
30481
30482         return;
30483             
30484     },
30485     
30486     setThumbBoxPosition : function()
30487     {
30488         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30489         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30490         
30491         this.thumbEl.setLeft(x);
30492         this.thumbEl.setTop(y);
30493         
30494     },
30495     
30496     baseRotateLevel : function()
30497     {
30498         this.baseRotate = 1;
30499         
30500         if(
30501                 typeof(this.exif) != 'undefined' &&
30502                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30503                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30504         ){
30505             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30506         }
30507         
30508         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30509         
30510     },
30511     
30512     baseScaleLevel : function()
30513     {
30514         var width, height;
30515         
30516         if(this.isDocument){
30517             
30518             if(this.baseRotate == 6 || this.baseRotate == 8){
30519             
30520                 height = this.thumbEl.getHeight();
30521                 this.baseScale = height / this.imageEl.OriginWidth;
30522
30523                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30524                     width = this.thumbEl.getWidth();
30525                     this.baseScale = width / this.imageEl.OriginHeight;
30526                 }
30527
30528                 return;
30529             }
30530
30531             height = this.thumbEl.getHeight();
30532             this.baseScale = height / this.imageEl.OriginHeight;
30533
30534             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30535                 width = this.thumbEl.getWidth();
30536                 this.baseScale = width / this.imageEl.OriginWidth;
30537             }
30538
30539             return;
30540         }
30541         
30542         if(this.baseRotate == 6 || this.baseRotate == 8){
30543             
30544             width = this.thumbEl.getHeight();
30545             this.baseScale = width / this.imageEl.OriginHeight;
30546             
30547             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30548                 height = this.thumbEl.getWidth();
30549                 this.baseScale = height / this.imageEl.OriginHeight;
30550             }
30551             
30552             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30553                 height = this.thumbEl.getWidth();
30554                 this.baseScale = height / this.imageEl.OriginHeight;
30555                 
30556                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30557                     width = this.thumbEl.getHeight();
30558                     this.baseScale = width / this.imageEl.OriginWidth;
30559                 }
30560             }
30561             
30562             return;
30563         }
30564         
30565         width = this.thumbEl.getWidth();
30566         this.baseScale = width / this.imageEl.OriginWidth;
30567         
30568         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30569             height = this.thumbEl.getHeight();
30570             this.baseScale = height / this.imageEl.OriginHeight;
30571         }
30572         
30573         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30574             
30575             height = this.thumbEl.getHeight();
30576             this.baseScale = height / this.imageEl.OriginHeight;
30577             
30578             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30579                 width = this.thumbEl.getWidth();
30580                 this.baseScale = width / this.imageEl.OriginWidth;
30581             }
30582             
30583         }
30584         
30585         return;
30586     },
30587     
30588     getScaleLevel : function()
30589     {
30590         return this.baseScale * Math.pow(1.1, this.scale);
30591     },
30592     
30593     onTouchStart : function(e)
30594     {
30595         if(!this.canvasLoaded){
30596             this.beforeSelectFile(e);
30597             return;
30598         }
30599         
30600         var touches = e.browserEvent.touches;
30601         
30602         if(!touches){
30603             return;
30604         }
30605         
30606         if(touches.length == 1){
30607             this.onMouseDown(e);
30608             return;
30609         }
30610         
30611         if(touches.length != 2){
30612             return;
30613         }
30614         
30615         var coords = [];
30616         
30617         for(var i = 0, finger; finger = touches[i]; i++){
30618             coords.push(finger.pageX, finger.pageY);
30619         }
30620         
30621         var x = Math.pow(coords[0] - coords[2], 2);
30622         var y = Math.pow(coords[1] - coords[3], 2);
30623         
30624         this.startDistance = Math.sqrt(x + y);
30625         
30626         this.startScale = this.scale;
30627         
30628         this.pinching = true;
30629         this.dragable = false;
30630         
30631     },
30632     
30633     onTouchMove : function(e)
30634     {
30635         if(!this.pinching && !this.dragable){
30636             return;
30637         }
30638         
30639         var touches = e.browserEvent.touches;
30640         
30641         if(!touches){
30642             return;
30643         }
30644         
30645         if(this.dragable){
30646             this.onMouseMove(e);
30647             return;
30648         }
30649         
30650         var coords = [];
30651         
30652         for(var i = 0, finger; finger = touches[i]; i++){
30653             coords.push(finger.pageX, finger.pageY);
30654         }
30655         
30656         var x = Math.pow(coords[0] - coords[2], 2);
30657         var y = Math.pow(coords[1] - coords[3], 2);
30658         
30659         this.endDistance = Math.sqrt(x + y);
30660         
30661         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30662         
30663         if(!this.zoomable()){
30664             this.scale = this.startScale;
30665             return;
30666         }
30667         
30668         this.draw();
30669         
30670     },
30671     
30672     onTouchEnd : function(e)
30673     {
30674         this.pinching = false;
30675         this.dragable = false;
30676         
30677     },
30678     
30679     process : function(file, crop)
30680     {
30681         if(this.loadMask){
30682             this.maskEl.mask(this.loadingText);
30683         }
30684         
30685         this.xhr = new XMLHttpRequest();
30686         
30687         file.xhr = this.xhr;
30688
30689         this.xhr.open(this.method, this.url, true);
30690         
30691         var headers = {
30692             "Accept": "application/json",
30693             "Cache-Control": "no-cache",
30694             "X-Requested-With": "XMLHttpRequest"
30695         };
30696         
30697         for (var headerName in headers) {
30698             var headerValue = headers[headerName];
30699             if (headerValue) {
30700                 this.xhr.setRequestHeader(headerName, headerValue);
30701             }
30702         }
30703         
30704         var _this = this;
30705         
30706         this.xhr.onload = function()
30707         {
30708             _this.xhrOnLoad(_this.xhr);
30709         }
30710         
30711         this.xhr.onerror = function()
30712         {
30713             _this.xhrOnError(_this.xhr);
30714         }
30715         
30716         var formData = new FormData();
30717
30718         formData.append('returnHTML', 'NO');
30719         
30720         if(crop){
30721             formData.append('crop', crop);
30722         }
30723         
30724         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30725             formData.append(this.paramName, file, file.name);
30726         }
30727         
30728         if(typeof(file.filename) != 'undefined'){
30729             formData.append('filename', file.filename);
30730         }
30731         
30732         if(typeof(file.mimetype) != 'undefined'){
30733             formData.append('mimetype', file.mimetype);
30734         }
30735         
30736         if(this.fireEvent('arrange', this, formData) != false){
30737             this.xhr.send(formData);
30738         };
30739     },
30740     
30741     xhrOnLoad : function(xhr)
30742     {
30743         if(this.loadMask){
30744             this.maskEl.unmask();
30745         }
30746         
30747         if (xhr.readyState !== 4) {
30748             this.fireEvent('exception', this, xhr);
30749             return;
30750         }
30751
30752         var response = Roo.decode(xhr.responseText);
30753         
30754         if(!response.success){
30755             this.fireEvent('exception', this, xhr);
30756             return;
30757         }
30758         
30759         var response = Roo.decode(xhr.responseText);
30760         
30761         this.fireEvent('upload', this, response);
30762         
30763     },
30764     
30765     xhrOnError : function()
30766     {
30767         if(this.loadMask){
30768             this.maskEl.unmask();
30769         }
30770         
30771         Roo.log('xhr on error');
30772         
30773         var response = Roo.decode(xhr.responseText);
30774           
30775         Roo.log(response);
30776         
30777     },
30778     
30779     prepare : function(file)
30780     {   
30781         if(this.loadMask){
30782             this.maskEl.mask(this.loadingText);
30783         }
30784         
30785         this.file = false;
30786         this.exif = {};
30787         
30788         if(typeof(file) === 'string'){
30789             this.loadCanvas(file);
30790             return;
30791         }
30792         
30793         if(!file || !this.urlAPI){
30794             return;
30795         }
30796         
30797         this.file = file;
30798         this.cropType = file.type;
30799         
30800         var _this = this;
30801         
30802         if(this.fireEvent('prepare', this, this.file) != false){
30803             
30804             var reader = new FileReader();
30805             
30806             reader.onload = function (e) {
30807                 if (e.target.error) {
30808                     Roo.log(e.target.error);
30809                     return;
30810                 }
30811                 
30812                 var buffer = e.target.result,
30813                     dataView = new DataView(buffer),
30814                     offset = 2,
30815                     maxOffset = dataView.byteLength - 4,
30816                     markerBytes,
30817                     markerLength;
30818                 
30819                 if (dataView.getUint16(0) === 0xffd8) {
30820                     while (offset < maxOffset) {
30821                         markerBytes = dataView.getUint16(offset);
30822                         
30823                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30824                             markerLength = dataView.getUint16(offset + 2) + 2;
30825                             if (offset + markerLength > dataView.byteLength) {
30826                                 Roo.log('Invalid meta data: Invalid segment size.');
30827                                 break;
30828                             }
30829                             
30830                             if(markerBytes == 0xffe1){
30831                                 _this.parseExifData(
30832                                     dataView,
30833                                     offset,
30834                                     markerLength
30835                                 );
30836                             }
30837                             
30838                             offset += markerLength;
30839                             
30840                             continue;
30841                         }
30842                         
30843                         break;
30844                     }
30845                     
30846                 }
30847                 
30848                 var url = _this.urlAPI.createObjectURL(_this.file);
30849                 
30850                 _this.loadCanvas(url);
30851                 
30852                 return;
30853             }
30854             
30855             reader.readAsArrayBuffer(this.file);
30856             
30857         }
30858         
30859     },
30860     
30861     parseExifData : function(dataView, offset, length)
30862     {
30863         var tiffOffset = offset + 10,
30864             littleEndian,
30865             dirOffset;
30866     
30867         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30868             // No Exif data, might be XMP data instead
30869             return;
30870         }
30871         
30872         // Check for the ASCII code for "Exif" (0x45786966):
30873         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30874             // No Exif data, might be XMP data instead
30875             return;
30876         }
30877         if (tiffOffset + 8 > dataView.byteLength) {
30878             Roo.log('Invalid Exif data: Invalid segment size.');
30879             return;
30880         }
30881         // Check for the two null bytes:
30882         if (dataView.getUint16(offset + 8) !== 0x0000) {
30883             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30884             return;
30885         }
30886         // Check the byte alignment:
30887         switch (dataView.getUint16(tiffOffset)) {
30888         case 0x4949:
30889             littleEndian = true;
30890             break;
30891         case 0x4D4D:
30892             littleEndian = false;
30893             break;
30894         default:
30895             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30896             return;
30897         }
30898         // Check for the TIFF tag marker (0x002A):
30899         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30900             Roo.log('Invalid Exif data: Missing TIFF marker.');
30901             return;
30902         }
30903         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30904         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30905         
30906         this.parseExifTags(
30907             dataView,
30908             tiffOffset,
30909             tiffOffset + dirOffset,
30910             littleEndian
30911         );
30912     },
30913     
30914     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30915     {
30916         var tagsNumber,
30917             dirEndOffset,
30918             i;
30919         if (dirOffset + 6 > dataView.byteLength) {
30920             Roo.log('Invalid Exif data: Invalid directory offset.');
30921             return;
30922         }
30923         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30924         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30925         if (dirEndOffset + 4 > dataView.byteLength) {
30926             Roo.log('Invalid Exif data: Invalid directory size.');
30927             return;
30928         }
30929         for (i = 0; i < tagsNumber; i += 1) {
30930             this.parseExifTag(
30931                 dataView,
30932                 tiffOffset,
30933                 dirOffset + 2 + 12 * i, // tag offset
30934                 littleEndian
30935             );
30936         }
30937         // Return the offset to the next directory:
30938         return dataView.getUint32(dirEndOffset, littleEndian);
30939     },
30940     
30941     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30942     {
30943         var tag = dataView.getUint16(offset, littleEndian);
30944         
30945         this.exif[tag] = this.getExifValue(
30946             dataView,
30947             tiffOffset,
30948             offset,
30949             dataView.getUint16(offset + 2, littleEndian), // tag type
30950             dataView.getUint32(offset + 4, littleEndian), // tag length
30951             littleEndian
30952         );
30953     },
30954     
30955     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30956     {
30957         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30958             tagSize,
30959             dataOffset,
30960             values,
30961             i,
30962             str,
30963             c;
30964     
30965         if (!tagType) {
30966             Roo.log('Invalid Exif data: Invalid tag type.');
30967             return;
30968         }
30969         
30970         tagSize = tagType.size * length;
30971         // Determine if the value is contained in the dataOffset bytes,
30972         // or if the value at the dataOffset is a pointer to the actual data:
30973         dataOffset = tagSize > 4 ?
30974                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30975         if (dataOffset + tagSize > dataView.byteLength) {
30976             Roo.log('Invalid Exif data: Invalid data offset.');
30977             return;
30978         }
30979         if (length === 1) {
30980             return tagType.getValue(dataView, dataOffset, littleEndian);
30981         }
30982         values = [];
30983         for (i = 0; i < length; i += 1) {
30984             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30985         }
30986         
30987         if (tagType.ascii) {
30988             str = '';
30989             // Concatenate the chars:
30990             for (i = 0; i < values.length; i += 1) {
30991                 c = values[i];
30992                 // Ignore the terminating NULL byte(s):
30993                 if (c === '\u0000') {
30994                     break;
30995                 }
30996                 str += c;
30997             }
30998             return str;
30999         }
31000         return values;
31001     }
31002     
31003 });
31004
31005 Roo.apply(Roo.bootstrap.UploadCropbox, {
31006     tags : {
31007         'Orientation': 0x0112
31008     },
31009     
31010     Orientation: {
31011             1: 0, //'top-left',
31012 //            2: 'top-right',
31013             3: 180, //'bottom-right',
31014 //            4: 'bottom-left',
31015 //            5: 'left-top',
31016             6: 90, //'right-top',
31017 //            7: 'right-bottom',
31018             8: 270 //'left-bottom'
31019     },
31020     
31021     exifTagTypes : {
31022         // byte, 8-bit unsigned int:
31023         1: {
31024             getValue: function (dataView, dataOffset) {
31025                 return dataView.getUint8(dataOffset);
31026             },
31027             size: 1
31028         },
31029         // ascii, 8-bit byte:
31030         2: {
31031             getValue: function (dataView, dataOffset) {
31032                 return String.fromCharCode(dataView.getUint8(dataOffset));
31033             },
31034             size: 1,
31035             ascii: true
31036         },
31037         // short, 16 bit int:
31038         3: {
31039             getValue: function (dataView, dataOffset, littleEndian) {
31040                 return dataView.getUint16(dataOffset, littleEndian);
31041             },
31042             size: 2
31043         },
31044         // long, 32 bit int:
31045         4: {
31046             getValue: function (dataView, dataOffset, littleEndian) {
31047                 return dataView.getUint32(dataOffset, littleEndian);
31048             },
31049             size: 4
31050         },
31051         // rational = two long values, first is numerator, second is denominator:
31052         5: {
31053             getValue: function (dataView, dataOffset, littleEndian) {
31054                 return dataView.getUint32(dataOffset, littleEndian) /
31055                     dataView.getUint32(dataOffset + 4, littleEndian);
31056             },
31057             size: 8
31058         },
31059         // slong, 32 bit signed int:
31060         9: {
31061             getValue: function (dataView, dataOffset, littleEndian) {
31062                 return dataView.getInt32(dataOffset, littleEndian);
31063             },
31064             size: 4
31065         },
31066         // srational, two slongs, first is numerator, second is denominator:
31067         10: {
31068             getValue: function (dataView, dataOffset, littleEndian) {
31069                 return dataView.getInt32(dataOffset, littleEndian) /
31070                     dataView.getInt32(dataOffset + 4, littleEndian);
31071             },
31072             size: 8
31073         }
31074     },
31075     
31076     footer : {
31077         STANDARD : [
31078             {
31079                 tag : 'div',
31080                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31081                 action : 'rotate-left',
31082                 cn : [
31083                     {
31084                         tag : 'button',
31085                         cls : 'btn btn-default',
31086                         html : '<i class="fa fa-undo"></i>'
31087                     }
31088                 ]
31089             },
31090             {
31091                 tag : 'div',
31092                 cls : 'btn-group roo-upload-cropbox-picture',
31093                 action : 'picture',
31094                 cn : [
31095                     {
31096                         tag : 'button',
31097                         cls : 'btn btn-default',
31098                         html : '<i class="fa fa-picture-o"></i>'
31099                     }
31100                 ]
31101             },
31102             {
31103                 tag : 'div',
31104                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31105                 action : 'rotate-right',
31106                 cn : [
31107                     {
31108                         tag : 'button',
31109                         cls : 'btn btn-default',
31110                         html : '<i class="fa fa-repeat"></i>'
31111                     }
31112                 ]
31113             }
31114         ],
31115         DOCUMENT : [
31116             {
31117                 tag : 'div',
31118                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31119                 action : 'rotate-left',
31120                 cn : [
31121                     {
31122                         tag : 'button',
31123                         cls : 'btn btn-default',
31124                         html : '<i class="fa fa-undo"></i>'
31125                     }
31126                 ]
31127             },
31128             {
31129                 tag : 'div',
31130                 cls : 'btn-group roo-upload-cropbox-download',
31131                 action : 'download',
31132                 cn : [
31133                     {
31134                         tag : 'button',
31135                         cls : 'btn btn-default',
31136                         html : '<i class="fa fa-download"></i>'
31137                     }
31138                 ]
31139             },
31140             {
31141                 tag : 'div',
31142                 cls : 'btn-group roo-upload-cropbox-crop',
31143                 action : 'crop',
31144                 cn : [
31145                     {
31146                         tag : 'button',
31147                         cls : 'btn btn-default',
31148                         html : '<i class="fa fa-crop"></i>'
31149                     }
31150                 ]
31151             },
31152             {
31153                 tag : 'div',
31154                 cls : 'btn-group roo-upload-cropbox-trash',
31155                 action : 'trash',
31156                 cn : [
31157                     {
31158                         tag : 'button',
31159                         cls : 'btn btn-default',
31160                         html : '<i class="fa fa-trash"></i>'
31161                     }
31162                 ]
31163             },
31164             {
31165                 tag : 'div',
31166                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31167                 action : 'rotate-right',
31168                 cn : [
31169                     {
31170                         tag : 'button',
31171                         cls : 'btn btn-default',
31172                         html : '<i class="fa fa-repeat"></i>'
31173                     }
31174                 ]
31175             }
31176         ],
31177         ROTATOR : [
31178             {
31179                 tag : 'div',
31180                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31181                 action : 'rotate-left',
31182                 cn : [
31183                     {
31184                         tag : 'button',
31185                         cls : 'btn btn-default',
31186                         html : '<i class="fa fa-undo"></i>'
31187                     }
31188                 ]
31189             },
31190             {
31191                 tag : 'div',
31192                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31193                 action : 'rotate-right',
31194                 cn : [
31195                     {
31196                         tag : 'button',
31197                         cls : 'btn btn-default',
31198                         html : '<i class="fa fa-repeat"></i>'
31199                     }
31200                 ]
31201             }
31202         ]
31203     }
31204 });
31205
31206 /*
31207 * Licence: LGPL
31208 */
31209
31210 /**
31211  * @class Roo.bootstrap.DocumentManager
31212  * @extends Roo.bootstrap.Component
31213  * Bootstrap DocumentManager class
31214  * @cfg {String} paramName default 'imageUpload'
31215  * @cfg {String} toolTipName default 'filename'
31216  * @cfg {String} method default POST
31217  * @cfg {String} url action url
31218  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31219  * @cfg {Boolean} multiple multiple upload default true
31220  * @cfg {Number} thumbSize default 300
31221  * @cfg {String} fieldLabel
31222  * @cfg {Number} labelWidth default 4
31223  * @cfg {String} labelAlign (left|top) default left
31224  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31225 * @cfg {Number} labellg set the width of label (1-12)
31226  * @cfg {Number} labelmd set the width of label (1-12)
31227  * @cfg {Number} labelsm set the width of label (1-12)
31228  * @cfg {Number} labelxs set the width of label (1-12)
31229  * 
31230  * @constructor
31231  * Create a new DocumentManager
31232  * @param {Object} config The config object
31233  */
31234
31235 Roo.bootstrap.DocumentManager = function(config){
31236     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31237     
31238     this.files = [];
31239     this.delegates = [];
31240     
31241     this.addEvents({
31242         /**
31243          * @event initial
31244          * Fire when initial the DocumentManager
31245          * @param {Roo.bootstrap.DocumentManager} this
31246          */
31247         "initial" : true,
31248         /**
31249          * @event inspect
31250          * inspect selected file
31251          * @param {Roo.bootstrap.DocumentManager} this
31252          * @param {File} file
31253          */
31254         "inspect" : true,
31255         /**
31256          * @event exception
31257          * Fire when xhr load exception
31258          * @param {Roo.bootstrap.DocumentManager} this
31259          * @param {XMLHttpRequest} xhr
31260          */
31261         "exception" : true,
31262         /**
31263          * @event afterupload
31264          * Fire when xhr load exception
31265          * @param {Roo.bootstrap.DocumentManager} this
31266          * @param {XMLHttpRequest} xhr
31267          */
31268         "afterupload" : true,
31269         /**
31270          * @event prepare
31271          * prepare the form data
31272          * @param {Roo.bootstrap.DocumentManager} this
31273          * @param {Object} formData
31274          */
31275         "prepare" : true,
31276         /**
31277          * @event remove
31278          * Fire when remove the file
31279          * @param {Roo.bootstrap.DocumentManager} this
31280          * @param {Object} file
31281          */
31282         "remove" : true,
31283         /**
31284          * @event refresh
31285          * Fire after refresh the file
31286          * @param {Roo.bootstrap.DocumentManager} this
31287          */
31288         "refresh" : true,
31289         /**
31290          * @event click
31291          * Fire after click the image
31292          * @param {Roo.bootstrap.DocumentManager} this
31293          * @param {Object} file
31294          */
31295         "click" : true,
31296         /**
31297          * @event edit
31298          * Fire when upload a image and editable set to true
31299          * @param {Roo.bootstrap.DocumentManager} this
31300          * @param {Object} file
31301          */
31302         "edit" : true,
31303         /**
31304          * @event beforeselectfile
31305          * Fire before select file
31306          * @param {Roo.bootstrap.DocumentManager} this
31307          */
31308         "beforeselectfile" : true,
31309         /**
31310          * @event process
31311          * Fire before process file
31312          * @param {Roo.bootstrap.DocumentManager} this
31313          * @param {Object} file
31314          */
31315         "process" : true,
31316         /**
31317          * @event previewrendered
31318          * Fire when preview rendered
31319          * @param {Roo.bootstrap.DocumentManager} this
31320          * @param {Object} file
31321          */
31322         "previewrendered" : true,
31323         /**
31324          */
31325         "previewResize" : true
31326         
31327     });
31328 };
31329
31330 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31331     
31332     boxes : 0,
31333     inputName : '',
31334     thumbSize : 300,
31335     multiple : true,
31336     files : false,
31337     method : 'POST',
31338     url : '',
31339     paramName : 'imageUpload',
31340     toolTipName : 'filename',
31341     fieldLabel : '',
31342     labelWidth : 4,
31343     labelAlign : 'left',
31344     editable : true,
31345     delegates : false,
31346     xhr : false, 
31347     
31348     labellg : 0,
31349     labelmd : 0,
31350     labelsm : 0,
31351     labelxs : 0,
31352     
31353     getAutoCreate : function()
31354     {   
31355         var managerWidget = {
31356             tag : 'div',
31357             cls : 'roo-document-manager',
31358             cn : [
31359                 {
31360                     tag : 'input',
31361                     cls : 'roo-document-manager-selector',
31362                     type : 'file'
31363                 },
31364                 {
31365                     tag : 'div',
31366                     cls : 'roo-document-manager-uploader',
31367                     cn : [
31368                         {
31369                             tag : 'div',
31370                             cls : 'roo-document-manager-upload-btn',
31371                             html : '<i class="fa fa-plus"></i>'
31372                         }
31373                     ]
31374                     
31375                 }
31376             ]
31377         };
31378         
31379         var content = [
31380             {
31381                 tag : 'div',
31382                 cls : 'column col-md-12',
31383                 cn : managerWidget
31384             }
31385         ];
31386         
31387         if(this.fieldLabel.length){
31388             
31389             content = [
31390                 {
31391                     tag : 'div',
31392                     cls : 'column col-md-12',
31393                     html : this.fieldLabel
31394                 },
31395                 {
31396                     tag : 'div',
31397                     cls : 'column col-md-12',
31398                     cn : managerWidget
31399                 }
31400             ];
31401
31402             if(this.labelAlign == 'left'){
31403                 content = [
31404                     {
31405                         tag : 'div',
31406                         cls : 'column',
31407                         html : this.fieldLabel
31408                     },
31409                     {
31410                         tag : 'div',
31411                         cls : 'column',
31412                         cn : managerWidget
31413                     }
31414                 ];
31415                 
31416                 if(this.labelWidth > 12){
31417                     content[0].style = "width: " + this.labelWidth + 'px';
31418                 }
31419
31420                 if(this.labelWidth < 13 && this.labelmd == 0){
31421                     this.labelmd = this.labelWidth;
31422                 }
31423
31424                 if(this.labellg > 0){
31425                     content[0].cls += ' col-lg-' + this.labellg;
31426                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31427                 }
31428
31429                 if(this.labelmd > 0){
31430                     content[0].cls += ' col-md-' + this.labelmd;
31431                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31432                 }
31433
31434                 if(this.labelsm > 0){
31435                     content[0].cls += ' col-sm-' + this.labelsm;
31436                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31437                 }
31438
31439                 if(this.labelxs > 0){
31440                     content[0].cls += ' col-xs-' + this.labelxs;
31441                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31442                 }
31443                 
31444             }
31445         }
31446         
31447         var cfg = {
31448             tag : 'div',
31449             cls : 'row clearfix',
31450             cn : content
31451         };
31452         
31453         return cfg;
31454         
31455     },
31456     
31457     initEvents : function()
31458     {
31459         this.managerEl = this.el.select('.roo-document-manager', true).first();
31460         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31461         
31462         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31463         this.selectorEl.hide();
31464         
31465         if(this.multiple){
31466             this.selectorEl.attr('multiple', 'multiple');
31467         }
31468         
31469         this.selectorEl.on('change', this.onFileSelected, this);
31470         
31471         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31472         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31473         
31474         this.uploader.on('click', this.onUploaderClick, this);
31475         
31476         this.renderProgressDialog();
31477         
31478         var _this = this;
31479         
31480         window.addEventListener("resize", function() { _this.refresh(); } );
31481         
31482         this.fireEvent('initial', this);
31483     },
31484     
31485     renderProgressDialog : function()
31486     {
31487         var _this = this;
31488         
31489         this.progressDialog = new Roo.bootstrap.Modal({
31490             cls : 'roo-document-manager-progress-dialog',
31491             allow_close : false,
31492             animate : false,
31493             title : '',
31494             buttons : [
31495                 {
31496                     name  :'cancel',
31497                     weight : 'danger',
31498                     html : 'Cancel'
31499                 }
31500             ], 
31501             listeners : { 
31502                 btnclick : function() {
31503                     _this.uploadCancel();
31504                     this.hide();
31505                 }
31506             }
31507         });
31508          
31509         this.progressDialog.render(Roo.get(document.body));
31510          
31511         this.progress = new Roo.bootstrap.Progress({
31512             cls : 'roo-document-manager-progress',
31513             active : true,
31514             striped : true
31515         });
31516         
31517         this.progress.render(this.progressDialog.getChildContainer());
31518         
31519         this.progressBar = new Roo.bootstrap.ProgressBar({
31520             cls : 'roo-document-manager-progress-bar',
31521             aria_valuenow : 0,
31522             aria_valuemin : 0,
31523             aria_valuemax : 12,
31524             panel : 'success'
31525         });
31526         
31527         this.progressBar.render(this.progress.getChildContainer());
31528     },
31529     
31530     onUploaderClick : function(e)
31531     {
31532         e.preventDefault();
31533      
31534         if(this.fireEvent('beforeselectfile', this) != false){
31535             this.selectorEl.dom.click();
31536         }
31537         
31538     },
31539     
31540     onFileSelected : function(e)
31541     {
31542         e.preventDefault();
31543         
31544         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31545             return;
31546         }
31547         
31548         Roo.each(this.selectorEl.dom.files, function(file){
31549             if(this.fireEvent('inspect', this, file) != false){
31550                 this.files.push(file);
31551             }
31552         }, this);
31553         
31554         this.queue();
31555         
31556     },
31557     
31558     queue : function()
31559     {
31560         this.selectorEl.dom.value = '';
31561         
31562         if(!this.files || !this.files.length){
31563             return;
31564         }
31565         
31566         if(this.boxes > 0 && this.files.length > this.boxes){
31567             this.files = this.files.slice(0, this.boxes);
31568         }
31569         
31570         this.uploader.show();
31571         
31572         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31573             this.uploader.hide();
31574         }
31575         
31576         var _this = this;
31577         
31578         var files = [];
31579         
31580         var docs = [];
31581         
31582         Roo.each(this.files, function(file){
31583             
31584             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31585                 var f = this.renderPreview(file);
31586                 files.push(f);
31587                 return;
31588             }
31589             
31590             if(file.type.indexOf('image') != -1){
31591                 this.delegates.push(
31592                     (function(){
31593                         _this.process(file);
31594                     }).createDelegate(this)
31595                 );
31596         
31597                 return;
31598             }
31599             
31600             docs.push(
31601                 (function(){
31602                     _this.process(file);
31603                 }).createDelegate(this)
31604             );
31605             
31606         }, this);
31607         
31608         this.files = files;
31609         
31610         this.delegates = this.delegates.concat(docs);
31611         
31612         if(!this.delegates.length){
31613             this.refresh();
31614             return;
31615         }
31616         
31617         this.progressBar.aria_valuemax = this.delegates.length;
31618         
31619         this.arrange();
31620         
31621         return;
31622     },
31623     
31624     arrange : function()
31625     {
31626         if(!this.delegates.length){
31627             this.progressDialog.hide();
31628             this.refresh();
31629             return;
31630         }
31631         
31632         var delegate = this.delegates.shift();
31633         
31634         this.progressDialog.show();
31635         
31636         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31637         
31638         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31639         
31640         delegate();
31641     },
31642     
31643     refresh : function()
31644     {
31645         this.uploader.show();
31646         
31647         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31648             this.uploader.hide();
31649         }
31650         
31651         Roo.isTouch ? this.closable(false) : this.closable(true);
31652         
31653         this.fireEvent('refresh', this);
31654     },
31655     
31656     onRemove : function(e, el, o)
31657     {
31658         e.preventDefault();
31659         
31660         this.fireEvent('remove', this, o);
31661         
31662     },
31663     
31664     remove : function(o)
31665     {
31666         var files = [];
31667         
31668         Roo.each(this.files, function(file){
31669             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31670                 files.push(file);
31671                 return;
31672             }
31673
31674             o.target.remove();
31675
31676         }, this);
31677         
31678         this.files = files;
31679         
31680         this.refresh();
31681     },
31682     
31683     clear : function()
31684     {
31685         Roo.each(this.files, function(file){
31686             if(!file.target){
31687                 return;
31688             }
31689             
31690             file.target.remove();
31691
31692         }, this);
31693         
31694         this.files = [];
31695         
31696         this.refresh();
31697     },
31698     
31699     onClick : function(e, el, o)
31700     {
31701         e.preventDefault();
31702         
31703         this.fireEvent('click', this, o);
31704         
31705     },
31706     
31707     closable : function(closable)
31708     {
31709         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31710             
31711             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31712             
31713             if(closable){
31714                 el.show();
31715                 return;
31716             }
31717             
31718             el.hide();
31719             
31720         }, this);
31721     },
31722     
31723     xhrOnLoad : function(xhr)
31724     {
31725         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31726             el.remove();
31727         }, this);
31728         
31729         if (xhr.readyState !== 4) {
31730             this.arrange();
31731             this.fireEvent('exception', this, xhr);
31732             return;
31733         }
31734
31735         var response = Roo.decode(xhr.responseText);
31736         
31737         if(!response.success){
31738             this.arrange();
31739             this.fireEvent('exception', this, xhr);
31740             return;
31741         }
31742         
31743         var file = this.renderPreview(response.data);
31744         
31745         this.files.push(file);
31746         
31747         this.arrange();
31748         
31749         this.fireEvent('afterupload', this, xhr);
31750         
31751     },
31752     
31753     xhrOnError : function(xhr)
31754     {
31755         Roo.log('xhr on error');
31756         
31757         var response = Roo.decode(xhr.responseText);
31758           
31759         Roo.log(response);
31760         
31761         this.arrange();
31762     },
31763     
31764     process : function(file)
31765     {
31766         if(this.fireEvent('process', this, file) !== false){
31767             if(this.editable && file.type.indexOf('image') != -1){
31768                 this.fireEvent('edit', this, file);
31769                 return;
31770             }
31771
31772             this.uploadStart(file, false);
31773
31774             return;
31775         }
31776         
31777     },
31778     
31779     uploadStart : function(file, crop)
31780     {
31781         this.xhr = new XMLHttpRequest();
31782         
31783         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31784             this.arrange();
31785             return;
31786         }
31787         
31788         file.xhr = this.xhr;
31789             
31790         this.managerEl.createChild({
31791             tag : 'div',
31792             cls : 'roo-document-manager-loading',
31793             cn : [
31794                 {
31795                     tag : 'div',
31796                     tooltip : file.name,
31797                     cls : 'roo-document-manager-thumb',
31798                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31799                 }
31800             ]
31801
31802         });
31803
31804         this.xhr.open(this.method, this.url, true);
31805         
31806         var headers = {
31807             "Accept": "application/json",
31808             "Cache-Control": "no-cache",
31809             "X-Requested-With": "XMLHttpRequest"
31810         };
31811         
31812         for (var headerName in headers) {
31813             var headerValue = headers[headerName];
31814             if (headerValue) {
31815                 this.xhr.setRequestHeader(headerName, headerValue);
31816             }
31817         }
31818         
31819         var _this = this;
31820         
31821         this.xhr.onload = function()
31822         {
31823             _this.xhrOnLoad(_this.xhr);
31824         }
31825         
31826         this.xhr.onerror = function()
31827         {
31828             _this.xhrOnError(_this.xhr);
31829         }
31830         
31831         var formData = new FormData();
31832
31833         formData.append('returnHTML', 'NO');
31834         
31835         if(crop){
31836             formData.append('crop', crop);
31837         }
31838         
31839         formData.append(this.paramName, file, file.name);
31840         
31841         var options = {
31842             file : file, 
31843             manually : false
31844         };
31845         
31846         if(this.fireEvent('prepare', this, formData, options) != false){
31847             
31848             if(options.manually){
31849                 return;
31850             }
31851             
31852             this.xhr.send(formData);
31853             return;
31854         };
31855         
31856         this.uploadCancel();
31857     },
31858     
31859     uploadCancel : function()
31860     {
31861         if (this.xhr) {
31862             this.xhr.abort();
31863         }
31864         
31865         this.delegates = [];
31866         
31867         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31868             el.remove();
31869         }, this);
31870         
31871         this.arrange();
31872     },
31873     
31874     renderPreview : function(file)
31875     {
31876         if(typeof(file.target) != 'undefined' && file.target){
31877             return file;
31878         }
31879         
31880         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31881         
31882         var previewEl = this.managerEl.createChild({
31883             tag : 'div',
31884             cls : 'roo-document-manager-preview',
31885             cn : [
31886                 {
31887                     tag : 'div',
31888                     tooltip : file[this.toolTipName],
31889                     cls : 'roo-document-manager-thumb',
31890                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31891                 },
31892                 {
31893                     tag : 'button',
31894                     cls : 'close',
31895                     html : '<i class="fa fa-times-circle"></i>'
31896                 }
31897             ]
31898         });
31899
31900         var close = previewEl.select('button.close', true).first();
31901
31902         close.on('click', this.onRemove, this, file);
31903
31904         file.target = previewEl;
31905
31906         var image = previewEl.select('img', true).first();
31907         
31908         var _this = this;
31909         
31910         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31911         
31912         image.on('click', this.onClick, this, file);
31913         
31914         this.fireEvent('previewrendered', this, file);
31915         
31916         return file;
31917         
31918     },
31919     
31920     onPreviewLoad : function(file, image)
31921     {
31922         if(typeof(file.target) == 'undefined' || !file.target){
31923             return;
31924         }
31925         
31926         var width = image.dom.naturalWidth || image.dom.width;
31927         var height = image.dom.naturalHeight || image.dom.height;
31928         
31929         if(!this.previewResize) {
31930             return;
31931         }
31932         
31933         if(width > height){
31934             file.target.addClass('wide');
31935             return;
31936         }
31937         
31938         file.target.addClass('tall');
31939         return;
31940         
31941     },
31942     
31943     uploadFromSource : function(file, crop)
31944     {
31945         this.xhr = new XMLHttpRequest();
31946         
31947         this.managerEl.createChild({
31948             tag : 'div',
31949             cls : 'roo-document-manager-loading',
31950             cn : [
31951                 {
31952                     tag : 'div',
31953                     tooltip : file.name,
31954                     cls : 'roo-document-manager-thumb',
31955                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31956                 }
31957             ]
31958
31959         });
31960
31961         this.xhr.open(this.method, this.url, true);
31962         
31963         var headers = {
31964             "Accept": "application/json",
31965             "Cache-Control": "no-cache",
31966             "X-Requested-With": "XMLHttpRequest"
31967         };
31968         
31969         for (var headerName in headers) {
31970             var headerValue = headers[headerName];
31971             if (headerValue) {
31972                 this.xhr.setRequestHeader(headerName, headerValue);
31973             }
31974         }
31975         
31976         var _this = this;
31977         
31978         this.xhr.onload = function()
31979         {
31980             _this.xhrOnLoad(_this.xhr);
31981         }
31982         
31983         this.xhr.onerror = function()
31984         {
31985             _this.xhrOnError(_this.xhr);
31986         }
31987         
31988         var formData = new FormData();
31989
31990         formData.append('returnHTML', 'NO');
31991         
31992         formData.append('crop', crop);
31993         
31994         if(typeof(file.filename) != 'undefined'){
31995             formData.append('filename', file.filename);
31996         }
31997         
31998         if(typeof(file.mimetype) != 'undefined'){
31999             formData.append('mimetype', file.mimetype);
32000         }
32001         
32002         Roo.log(formData);
32003         
32004         if(this.fireEvent('prepare', this, formData) != false){
32005             this.xhr.send(formData);
32006         };
32007     }
32008 });
32009
32010 /*
32011 * Licence: LGPL
32012 */
32013
32014 /**
32015  * @class Roo.bootstrap.DocumentViewer
32016  * @extends Roo.bootstrap.Component
32017  * Bootstrap DocumentViewer class
32018  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32019  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32020  * 
32021  * @constructor
32022  * Create a new DocumentViewer
32023  * @param {Object} config The config object
32024  */
32025
32026 Roo.bootstrap.DocumentViewer = function(config){
32027     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32028     
32029     this.addEvents({
32030         /**
32031          * @event initial
32032          * Fire after initEvent
32033          * @param {Roo.bootstrap.DocumentViewer} this
32034          */
32035         "initial" : true,
32036         /**
32037          * @event click
32038          * Fire after click
32039          * @param {Roo.bootstrap.DocumentViewer} this
32040          */
32041         "click" : true,
32042         /**
32043          * @event download
32044          * Fire after download button
32045          * @param {Roo.bootstrap.DocumentViewer} this
32046          */
32047         "download" : true,
32048         /**
32049          * @event trash
32050          * Fire after trash button
32051          * @param {Roo.bootstrap.DocumentViewer} this
32052          */
32053         "trash" : true
32054         
32055     });
32056 };
32057
32058 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32059     
32060     showDownload : true,
32061     
32062     showTrash : true,
32063     
32064     getAutoCreate : function()
32065     {
32066         var cfg = {
32067             tag : 'div',
32068             cls : 'roo-document-viewer',
32069             cn : [
32070                 {
32071                     tag : 'div',
32072                     cls : 'roo-document-viewer-body',
32073                     cn : [
32074                         {
32075                             tag : 'div',
32076                             cls : 'roo-document-viewer-thumb',
32077                             cn : [
32078                                 {
32079                                     tag : 'img',
32080                                     cls : 'roo-document-viewer-image'
32081                                 }
32082                             ]
32083                         }
32084                     ]
32085                 },
32086                 {
32087                     tag : 'div',
32088                     cls : 'roo-document-viewer-footer',
32089                     cn : {
32090                         tag : 'div',
32091                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32092                         cn : [
32093                             {
32094                                 tag : 'div',
32095                                 cls : 'btn-group roo-document-viewer-download',
32096                                 cn : [
32097                                     {
32098                                         tag : 'button',
32099                                         cls : 'btn btn-default',
32100                                         html : '<i class="fa fa-download"></i>'
32101                                     }
32102                                 ]
32103                             },
32104                             {
32105                                 tag : 'div',
32106                                 cls : 'btn-group roo-document-viewer-trash',
32107                                 cn : [
32108                                     {
32109                                         tag : 'button',
32110                                         cls : 'btn btn-default',
32111                                         html : '<i class="fa fa-trash"></i>'
32112                                     }
32113                                 ]
32114                             }
32115                         ]
32116                     }
32117                 }
32118             ]
32119         };
32120         
32121         return cfg;
32122     },
32123     
32124     initEvents : function()
32125     {
32126         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32127         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32128         
32129         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32130         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32131         
32132         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32133         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32134         
32135         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32136         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32137         
32138         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32139         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32140         
32141         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32142         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32143         
32144         this.bodyEl.on('click', this.onClick, this);
32145         this.downloadBtn.on('click', this.onDownload, this);
32146         this.trashBtn.on('click', this.onTrash, this);
32147         
32148         this.downloadBtn.hide();
32149         this.trashBtn.hide();
32150         
32151         if(this.showDownload){
32152             this.downloadBtn.show();
32153         }
32154         
32155         if(this.showTrash){
32156             this.trashBtn.show();
32157         }
32158         
32159         if(!this.showDownload && !this.showTrash) {
32160             this.footerEl.hide();
32161         }
32162         
32163     },
32164     
32165     initial : function()
32166     {
32167         this.fireEvent('initial', this);
32168         
32169     },
32170     
32171     onClick : function(e)
32172     {
32173         e.preventDefault();
32174         
32175         this.fireEvent('click', this);
32176     },
32177     
32178     onDownload : function(e)
32179     {
32180         e.preventDefault();
32181         
32182         this.fireEvent('download', this);
32183     },
32184     
32185     onTrash : function(e)
32186     {
32187         e.preventDefault();
32188         
32189         this.fireEvent('trash', this);
32190     }
32191     
32192 });
32193 /*
32194  * - LGPL
32195  *
32196  * nav progress bar
32197  * 
32198  */
32199
32200 /**
32201  * @class Roo.bootstrap.NavProgressBar
32202  * @extends Roo.bootstrap.Component
32203  * Bootstrap NavProgressBar class
32204  * 
32205  * @constructor
32206  * Create a new nav progress bar
32207  * @param {Object} config The config object
32208  */
32209
32210 Roo.bootstrap.NavProgressBar = function(config){
32211     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32212
32213     this.bullets = this.bullets || [];
32214    
32215 //    Roo.bootstrap.NavProgressBar.register(this);
32216      this.addEvents({
32217         /**
32218              * @event changed
32219              * Fires when the active item changes
32220              * @param {Roo.bootstrap.NavProgressBar} this
32221              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32222              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32223          */
32224         'changed': true
32225      });
32226     
32227 };
32228
32229 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32230     
32231     bullets : [],
32232     barItems : [],
32233     
32234     getAutoCreate : function()
32235     {
32236         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32237         
32238         cfg = {
32239             tag : 'div',
32240             cls : 'roo-navigation-bar-group',
32241             cn : [
32242                 {
32243                     tag : 'div',
32244                     cls : 'roo-navigation-top-bar'
32245                 },
32246                 {
32247                     tag : 'div',
32248                     cls : 'roo-navigation-bullets-bar',
32249                     cn : [
32250                         {
32251                             tag : 'ul',
32252                             cls : 'roo-navigation-bar'
32253                         }
32254                     ]
32255                 },
32256                 
32257                 {
32258                     tag : 'div',
32259                     cls : 'roo-navigation-bottom-bar'
32260                 }
32261             ]
32262             
32263         };
32264         
32265         return cfg;
32266         
32267     },
32268     
32269     initEvents: function() 
32270     {
32271         
32272     },
32273     
32274     onRender : function(ct, position) 
32275     {
32276         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32277         
32278         if(this.bullets.length){
32279             Roo.each(this.bullets, function(b){
32280                this.addItem(b);
32281             }, this);
32282         }
32283         
32284         this.format();
32285         
32286     },
32287     
32288     addItem : function(cfg)
32289     {
32290         var item = new Roo.bootstrap.NavProgressItem(cfg);
32291         
32292         item.parentId = this.id;
32293         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32294         
32295         if(cfg.html){
32296             var top = new Roo.bootstrap.Element({
32297                 tag : 'div',
32298                 cls : 'roo-navigation-bar-text'
32299             });
32300             
32301             var bottom = new Roo.bootstrap.Element({
32302                 tag : 'div',
32303                 cls : 'roo-navigation-bar-text'
32304             });
32305             
32306             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32307             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32308             
32309             var topText = new Roo.bootstrap.Element({
32310                 tag : 'span',
32311                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32312             });
32313             
32314             var bottomText = new Roo.bootstrap.Element({
32315                 tag : 'span',
32316                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32317             });
32318             
32319             topText.onRender(top.el, null);
32320             bottomText.onRender(bottom.el, null);
32321             
32322             item.topEl = top;
32323             item.bottomEl = bottom;
32324         }
32325         
32326         this.barItems.push(item);
32327         
32328         return item;
32329     },
32330     
32331     getActive : function()
32332     {
32333         var active = false;
32334         
32335         Roo.each(this.barItems, function(v){
32336             
32337             if (!v.isActive()) {
32338                 return;
32339             }
32340             
32341             active = v;
32342             return false;
32343             
32344         });
32345         
32346         return active;
32347     },
32348     
32349     setActiveItem : function(item)
32350     {
32351         var prev = false;
32352         
32353         Roo.each(this.barItems, function(v){
32354             if (v.rid == item.rid) {
32355                 return ;
32356             }
32357             
32358             if (v.isActive()) {
32359                 v.setActive(false);
32360                 prev = v;
32361             }
32362         });
32363
32364         item.setActive(true);
32365         
32366         this.fireEvent('changed', this, item, prev);
32367     },
32368     
32369     getBarItem: function(rid)
32370     {
32371         var ret = false;
32372         
32373         Roo.each(this.barItems, function(e) {
32374             if (e.rid != rid) {
32375                 return;
32376             }
32377             
32378             ret =  e;
32379             return false;
32380         });
32381         
32382         return ret;
32383     },
32384     
32385     indexOfItem : function(item)
32386     {
32387         var index = false;
32388         
32389         Roo.each(this.barItems, function(v, i){
32390             
32391             if (v.rid != item.rid) {
32392                 return;
32393             }
32394             
32395             index = i;
32396             return false
32397         });
32398         
32399         return index;
32400     },
32401     
32402     setActiveNext : function()
32403     {
32404         var i = this.indexOfItem(this.getActive());
32405         
32406         if (i > this.barItems.length) {
32407             return;
32408         }
32409         
32410         this.setActiveItem(this.barItems[i+1]);
32411     },
32412     
32413     setActivePrev : function()
32414     {
32415         var i = this.indexOfItem(this.getActive());
32416         
32417         if (i  < 1) {
32418             return;
32419         }
32420         
32421         this.setActiveItem(this.barItems[i-1]);
32422     },
32423     
32424     format : function()
32425     {
32426         if(!this.barItems.length){
32427             return;
32428         }
32429      
32430         var width = 100 / this.barItems.length;
32431         
32432         Roo.each(this.barItems, function(i){
32433             i.el.setStyle('width', width + '%');
32434             i.topEl.el.setStyle('width', width + '%');
32435             i.bottomEl.el.setStyle('width', width + '%');
32436         }, this);
32437         
32438     }
32439     
32440 });
32441 /*
32442  * - LGPL
32443  *
32444  * Nav Progress Item
32445  * 
32446  */
32447
32448 /**
32449  * @class Roo.bootstrap.NavProgressItem
32450  * @extends Roo.bootstrap.Component
32451  * Bootstrap NavProgressItem class
32452  * @cfg {String} rid the reference id
32453  * @cfg {Boolean} active (true|false) Is item active default false
32454  * @cfg {Boolean} disabled (true|false) Is item active default false
32455  * @cfg {String} html
32456  * @cfg {String} position (top|bottom) text position default bottom
32457  * @cfg {String} icon show icon instead of number
32458  * 
32459  * @constructor
32460  * Create a new NavProgressItem
32461  * @param {Object} config The config object
32462  */
32463 Roo.bootstrap.NavProgressItem = function(config){
32464     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32465     this.addEvents({
32466         // raw events
32467         /**
32468          * @event click
32469          * The raw click event for the entire grid.
32470          * @param {Roo.bootstrap.NavProgressItem} this
32471          * @param {Roo.EventObject} e
32472          */
32473         "click" : true
32474     });
32475    
32476 };
32477
32478 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32479     
32480     rid : '',
32481     active : false,
32482     disabled : false,
32483     html : '',
32484     position : 'bottom',
32485     icon : false,
32486     
32487     getAutoCreate : function()
32488     {
32489         var iconCls = 'roo-navigation-bar-item-icon';
32490         
32491         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32492         
32493         var cfg = {
32494             tag: 'li',
32495             cls: 'roo-navigation-bar-item',
32496             cn : [
32497                 {
32498                     tag : 'i',
32499                     cls : iconCls
32500                 }
32501             ]
32502         };
32503         
32504         if(this.active){
32505             cfg.cls += ' active';
32506         }
32507         if(this.disabled){
32508             cfg.cls += ' disabled';
32509         }
32510         
32511         return cfg;
32512     },
32513     
32514     disable : function()
32515     {
32516         this.setDisabled(true);
32517     },
32518     
32519     enable : function()
32520     {
32521         this.setDisabled(false);
32522     },
32523     
32524     initEvents: function() 
32525     {
32526         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32527         
32528         this.iconEl.on('click', this.onClick, this);
32529     },
32530     
32531     onClick : function(e)
32532     {
32533         e.preventDefault();
32534         
32535         if(this.disabled){
32536             return;
32537         }
32538         
32539         if(this.fireEvent('click', this, e) === false){
32540             return;
32541         };
32542         
32543         this.parent().setActiveItem(this);
32544     },
32545     
32546     isActive: function () 
32547     {
32548         return this.active;
32549     },
32550     
32551     setActive : function(state)
32552     {
32553         if(this.active == state){
32554             return;
32555         }
32556         
32557         this.active = state;
32558         
32559         if (state) {
32560             this.el.addClass('active');
32561             return;
32562         }
32563         
32564         this.el.removeClass('active');
32565         
32566         return;
32567     },
32568     
32569     setDisabled : function(state)
32570     {
32571         if(this.disabled == state){
32572             return;
32573         }
32574         
32575         this.disabled = state;
32576         
32577         if (state) {
32578             this.el.addClass('disabled');
32579             return;
32580         }
32581         
32582         this.el.removeClass('disabled');
32583     },
32584     
32585     tooltipEl : function()
32586     {
32587         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32588     }
32589 });
32590  
32591
32592  /*
32593  * - LGPL
32594  *
32595  * FieldLabel
32596  * 
32597  */
32598
32599 /**
32600  * @class Roo.bootstrap.FieldLabel
32601  * @extends Roo.bootstrap.Component
32602  * Bootstrap FieldLabel class
32603  * @cfg {String} html contents of the element
32604  * @cfg {String} tag tag of the element default label
32605  * @cfg {String} cls class of the element
32606  * @cfg {String} target label target 
32607  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32608  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32609  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32610  * @cfg {String} iconTooltip default "This field is required"
32611  * @cfg {String} indicatorpos (left|right) default left
32612  * 
32613  * @constructor
32614  * Create a new FieldLabel
32615  * @param {Object} config The config object
32616  */
32617
32618 Roo.bootstrap.FieldLabel = function(config){
32619     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32620     
32621     this.addEvents({
32622             /**
32623              * @event invalid
32624              * Fires after the field has been marked as invalid.
32625              * @param {Roo.form.FieldLabel} this
32626              * @param {String} msg The validation message
32627              */
32628             invalid : true,
32629             /**
32630              * @event valid
32631              * Fires after the field has been validated with no errors.
32632              * @param {Roo.form.FieldLabel} this
32633              */
32634             valid : true
32635         });
32636 };
32637
32638 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32639     
32640     tag: 'label',
32641     cls: '',
32642     html: '',
32643     target: '',
32644     allowBlank : true,
32645     invalidClass : 'has-warning',
32646     validClass : 'has-success',
32647     iconTooltip : 'This field is required',
32648     indicatorpos : 'left',
32649     
32650     getAutoCreate : function(){
32651         
32652         var cls = "";
32653         if (!this.allowBlank) {
32654             cls  = "visible";
32655         }
32656         
32657         var cfg = {
32658             tag : this.tag,
32659             cls : 'roo-bootstrap-field-label ' + this.cls,
32660             for : this.target,
32661             cn : [
32662                 {
32663                     tag : 'i',
32664                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32665                     tooltip : this.iconTooltip
32666                 },
32667                 {
32668                     tag : 'span',
32669                     html : this.html
32670                 }
32671             ] 
32672         };
32673         
32674         if(this.indicatorpos == 'right'){
32675             var cfg = {
32676                 tag : this.tag,
32677                 cls : 'roo-bootstrap-field-label ' + this.cls,
32678                 for : this.target,
32679                 cn : [
32680                     {
32681                         tag : 'span',
32682                         html : this.html
32683                     },
32684                     {
32685                         tag : 'i',
32686                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32687                         tooltip : this.iconTooltip
32688                     }
32689                 ] 
32690             };
32691         }
32692         
32693         return cfg;
32694     },
32695     
32696     initEvents: function() 
32697     {
32698         Roo.bootstrap.Element.superclass.initEvents.call(this);
32699         
32700         this.indicator = this.indicatorEl();
32701         
32702         if(this.indicator){
32703             this.indicator.removeClass('visible');
32704             this.indicator.addClass('invisible');
32705         }
32706         
32707         Roo.bootstrap.FieldLabel.register(this);
32708     },
32709     
32710     indicatorEl : function()
32711     {
32712         var indicator = this.el.select('i.roo-required-indicator',true).first();
32713         
32714         if(!indicator){
32715             return false;
32716         }
32717         
32718         return indicator;
32719         
32720     },
32721     
32722     /**
32723      * Mark this field as valid
32724      */
32725     markValid : function()
32726     {
32727         if(this.indicator){
32728             this.indicator.removeClass('visible');
32729             this.indicator.addClass('invisible');
32730         }
32731         if (Roo.bootstrap.version == 3) {
32732             this.el.removeClass(this.invalidClass);
32733             this.el.addClass(this.validClass);
32734         } else {
32735             this.el.removeClass('is-invalid');
32736             this.el.addClass('is-valid');
32737         }
32738         
32739         
32740         this.fireEvent('valid', this);
32741     },
32742     
32743     /**
32744      * Mark this field as invalid
32745      * @param {String} msg The validation message
32746      */
32747     markInvalid : function(msg)
32748     {
32749         if(this.indicator){
32750             this.indicator.removeClass('invisible');
32751             this.indicator.addClass('visible');
32752         }
32753           if (Roo.bootstrap.version == 3) {
32754             this.el.removeClass(this.validClass);
32755             this.el.addClass(this.invalidClass);
32756         } else {
32757             this.el.removeClass('is-valid');
32758             this.el.addClass('is-invalid');
32759         }
32760         
32761         
32762         this.fireEvent('invalid', this, msg);
32763     }
32764     
32765    
32766 });
32767
32768 Roo.apply(Roo.bootstrap.FieldLabel, {
32769     
32770     groups: {},
32771     
32772      /**
32773     * register a FieldLabel Group
32774     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32775     */
32776     register : function(label)
32777     {
32778         if(this.groups.hasOwnProperty(label.target)){
32779             return;
32780         }
32781      
32782         this.groups[label.target] = label;
32783         
32784     },
32785     /**
32786     * fetch a FieldLabel Group based on the target
32787     * @param {string} target
32788     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32789     */
32790     get: function(target) {
32791         if (typeof(this.groups[target]) == 'undefined') {
32792             return false;
32793         }
32794         
32795         return this.groups[target] ;
32796     }
32797 });
32798
32799  
32800
32801  /*
32802  * - LGPL
32803  *
32804  * page DateSplitField.
32805  * 
32806  */
32807
32808
32809 /**
32810  * @class Roo.bootstrap.DateSplitField
32811  * @extends Roo.bootstrap.Component
32812  * Bootstrap DateSplitField class
32813  * @cfg {string} fieldLabel - the label associated
32814  * @cfg {Number} labelWidth set the width of label (0-12)
32815  * @cfg {String} labelAlign (top|left)
32816  * @cfg {Boolean} dayAllowBlank (true|false) default false
32817  * @cfg {Boolean} monthAllowBlank (true|false) default false
32818  * @cfg {Boolean} yearAllowBlank (true|false) default false
32819  * @cfg {string} dayPlaceholder 
32820  * @cfg {string} monthPlaceholder
32821  * @cfg {string} yearPlaceholder
32822  * @cfg {string} dayFormat default 'd'
32823  * @cfg {string} monthFormat default 'm'
32824  * @cfg {string} yearFormat default 'Y'
32825  * @cfg {Number} labellg set the width of label (1-12)
32826  * @cfg {Number} labelmd set the width of label (1-12)
32827  * @cfg {Number} labelsm set the width of label (1-12)
32828  * @cfg {Number} labelxs set the width of label (1-12)
32829
32830  *     
32831  * @constructor
32832  * Create a new DateSplitField
32833  * @param {Object} config The config object
32834  */
32835
32836 Roo.bootstrap.DateSplitField = function(config){
32837     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32838     
32839     this.addEvents({
32840         // raw events
32841          /**
32842          * @event years
32843          * getting the data of years
32844          * @param {Roo.bootstrap.DateSplitField} this
32845          * @param {Object} years
32846          */
32847         "years" : true,
32848         /**
32849          * @event days
32850          * getting the data of days
32851          * @param {Roo.bootstrap.DateSplitField} this
32852          * @param {Object} days
32853          */
32854         "days" : true,
32855         /**
32856          * @event invalid
32857          * Fires after the field has been marked as invalid.
32858          * @param {Roo.form.Field} this
32859          * @param {String} msg The validation message
32860          */
32861         invalid : true,
32862        /**
32863          * @event valid
32864          * Fires after the field has been validated with no errors.
32865          * @param {Roo.form.Field} this
32866          */
32867         valid : true
32868     });
32869 };
32870
32871 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32872     
32873     fieldLabel : '',
32874     labelAlign : 'top',
32875     labelWidth : 3,
32876     dayAllowBlank : false,
32877     monthAllowBlank : false,
32878     yearAllowBlank : false,
32879     dayPlaceholder : '',
32880     monthPlaceholder : '',
32881     yearPlaceholder : '',
32882     dayFormat : 'd',
32883     monthFormat : 'm',
32884     yearFormat : 'Y',
32885     isFormField : true,
32886     labellg : 0,
32887     labelmd : 0,
32888     labelsm : 0,
32889     labelxs : 0,
32890     
32891     getAutoCreate : function()
32892     {
32893         var cfg = {
32894             tag : 'div',
32895             cls : 'row roo-date-split-field-group',
32896             cn : [
32897                 {
32898                     tag : 'input',
32899                     type : 'hidden',
32900                     cls : 'form-hidden-field roo-date-split-field-group-value',
32901                     name : this.name
32902                 }
32903             ]
32904         };
32905         
32906         var labelCls = 'col-md-12';
32907         var contentCls = 'col-md-4';
32908         
32909         if(this.fieldLabel){
32910             
32911             var label = {
32912                 tag : 'div',
32913                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32914                 cn : [
32915                     {
32916                         tag : 'label',
32917                         html : this.fieldLabel
32918                     }
32919                 ]
32920             };
32921             
32922             if(this.labelAlign == 'left'){
32923             
32924                 if(this.labelWidth > 12){
32925                     label.style = "width: " + this.labelWidth + 'px';
32926                 }
32927
32928                 if(this.labelWidth < 13 && this.labelmd == 0){
32929                     this.labelmd = this.labelWidth;
32930                 }
32931
32932                 if(this.labellg > 0){
32933                     labelCls = ' col-lg-' + this.labellg;
32934                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32935                 }
32936
32937                 if(this.labelmd > 0){
32938                     labelCls = ' col-md-' + this.labelmd;
32939                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32940                 }
32941
32942                 if(this.labelsm > 0){
32943                     labelCls = ' col-sm-' + this.labelsm;
32944                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32945                 }
32946
32947                 if(this.labelxs > 0){
32948                     labelCls = ' col-xs-' + this.labelxs;
32949                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32950                 }
32951             }
32952             
32953             label.cls += ' ' + labelCls;
32954             
32955             cfg.cn.push(label);
32956         }
32957         
32958         Roo.each(['day', 'month', 'year'], function(t){
32959             cfg.cn.push({
32960                 tag : 'div',
32961                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32962             });
32963         }, this);
32964         
32965         return cfg;
32966     },
32967     
32968     inputEl: function ()
32969     {
32970         return this.el.select('.roo-date-split-field-group-value', true).first();
32971     },
32972     
32973     onRender : function(ct, position) 
32974     {
32975         var _this = this;
32976         
32977         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32978         
32979         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32980         
32981         this.dayField = new Roo.bootstrap.ComboBox({
32982             allowBlank : this.dayAllowBlank,
32983             alwaysQuery : true,
32984             displayField : 'value',
32985             editable : false,
32986             fieldLabel : '',
32987             forceSelection : true,
32988             mode : 'local',
32989             placeholder : this.dayPlaceholder,
32990             selectOnFocus : true,
32991             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
32992             triggerAction : 'all',
32993             typeAhead : true,
32994             valueField : 'value',
32995             store : new Roo.data.SimpleStore({
32996                 data : (function() {    
32997                     var days = [];
32998                     _this.fireEvent('days', _this, days);
32999                     return days;
33000                 })(),
33001                 fields : [ 'value' ]
33002             }),
33003             listeners : {
33004                 select : function (_self, record, index)
33005                 {
33006                     _this.setValue(_this.getValue());
33007                 }
33008             }
33009         });
33010
33011         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33012         
33013         this.monthField = new Roo.bootstrap.MonthField({
33014             after : '<i class=\"fa fa-calendar\"></i>',
33015             allowBlank : this.monthAllowBlank,
33016             placeholder : this.monthPlaceholder,
33017             readOnly : true,
33018             listeners : {
33019                 render : function (_self)
33020                 {
33021                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33022                         e.preventDefault();
33023                         _self.focus();
33024                     });
33025                 },
33026                 select : function (_self, oldvalue, newvalue)
33027                 {
33028                     _this.setValue(_this.getValue());
33029                 }
33030             }
33031         });
33032         
33033         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33034         
33035         this.yearField = new Roo.bootstrap.ComboBox({
33036             allowBlank : this.yearAllowBlank,
33037             alwaysQuery : true,
33038             displayField : 'value',
33039             editable : false,
33040             fieldLabel : '',
33041             forceSelection : true,
33042             mode : 'local',
33043             placeholder : this.yearPlaceholder,
33044             selectOnFocus : true,
33045             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33046             triggerAction : 'all',
33047             typeAhead : true,
33048             valueField : 'value',
33049             store : new Roo.data.SimpleStore({
33050                 data : (function() {
33051                     var years = [];
33052                     _this.fireEvent('years', _this, years);
33053                     return years;
33054                 })(),
33055                 fields : [ 'value' ]
33056             }),
33057             listeners : {
33058                 select : function (_self, record, index)
33059                 {
33060                     _this.setValue(_this.getValue());
33061                 }
33062             }
33063         });
33064
33065         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33066     },
33067     
33068     setValue : function(v, format)
33069     {
33070         this.inputEl.dom.value = v;
33071         
33072         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33073         
33074         var d = Date.parseDate(v, f);
33075         
33076         if(!d){
33077             this.validate();
33078             return;
33079         }
33080         
33081         this.setDay(d.format(this.dayFormat));
33082         this.setMonth(d.format(this.monthFormat));
33083         this.setYear(d.format(this.yearFormat));
33084         
33085         this.validate();
33086         
33087         return;
33088     },
33089     
33090     setDay : function(v)
33091     {
33092         this.dayField.setValue(v);
33093         this.inputEl.dom.value = this.getValue();
33094         this.validate();
33095         return;
33096     },
33097     
33098     setMonth : function(v)
33099     {
33100         this.monthField.setValue(v, true);
33101         this.inputEl.dom.value = this.getValue();
33102         this.validate();
33103         return;
33104     },
33105     
33106     setYear : function(v)
33107     {
33108         this.yearField.setValue(v);
33109         this.inputEl.dom.value = this.getValue();
33110         this.validate();
33111         return;
33112     },
33113     
33114     getDay : function()
33115     {
33116         return this.dayField.getValue();
33117     },
33118     
33119     getMonth : function()
33120     {
33121         return this.monthField.getValue();
33122     },
33123     
33124     getYear : function()
33125     {
33126         return this.yearField.getValue();
33127     },
33128     
33129     getValue : function()
33130     {
33131         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33132         
33133         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33134         
33135         return date;
33136     },
33137     
33138     reset : function()
33139     {
33140         this.setDay('');
33141         this.setMonth('');
33142         this.setYear('');
33143         this.inputEl.dom.value = '';
33144         this.validate();
33145         return;
33146     },
33147     
33148     validate : function()
33149     {
33150         var d = this.dayField.validate();
33151         var m = this.monthField.validate();
33152         var y = this.yearField.validate();
33153         
33154         var valid = true;
33155         
33156         if(
33157                 (!this.dayAllowBlank && !d) ||
33158                 (!this.monthAllowBlank && !m) ||
33159                 (!this.yearAllowBlank && !y)
33160         ){
33161             valid = false;
33162         }
33163         
33164         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33165             return valid;
33166         }
33167         
33168         if(valid){
33169             this.markValid();
33170             return valid;
33171         }
33172         
33173         this.markInvalid();
33174         
33175         return valid;
33176     },
33177     
33178     markValid : function()
33179     {
33180         
33181         var label = this.el.select('label', true).first();
33182         var icon = this.el.select('i.fa-star', true).first();
33183
33184         if(label && icon){
33185             icon.remove();
33186         }
33187         
33188         this.fireEvent('valid', this);
33189     },
33190     
33191      /**
33192      * Mark this field as invalid
33193      * @param {String} msg The validation message
33194      */
33195     markInvalid : function(msg)
33196     {
33197         
33198         var label = this.el.select('label', true).first();
33199         var icon = this.el.select('i.fa-star', true).first();
33200
33201         if(label && !icon){
33202             this.el.select('.roo-date-split-field-label', true).createChild({
33203                 tag : 'i',
33204                 cls : 'text-danger fa fa-lg fa-star',
33205                 tooltip : 'This field is required',
33206                 style : 'margin-right:5px;'
33207             }, label, true);
33208         }
33209         
33210         this.fireEvent('invalid', this, msg);
33211     },
33212     
33213     clearInvalid : function()
33214     {
33215         var label = this.el.select('label', true).first();
33216         var icon = this.el.select('i.fa-star', true).first();
33217
33218         if(label && icon){
33219             icon.remove();
33220         }
33221         
33222         this.fireEvent('valid', this);
33223     },
33224     
33225     getName: function()
33226     {
33227         return this.name;
33228     }
33229     
33230 });
33231
33232  /**
33233  *
33234  * This is based on 
33235  * http://masonry.desandro.com
33236  *
33237  * The idea is to render all the bricks based on vertical width...
33238  *
33239  * The original code extends 'outlayer' - we might need to use that....
33240  * 
33241  */
33242
33243
33244 /**
33245  * @class Roo.bootstrap.LayoutMasonry
33246  * @extends Roo.bootstrap.Component
33247  * Bootstrap Layout Masonry class
33248  * 
33249  * @constructor
33250  * Create a new Element
33251  * @param {Object} config The config object
33252  */
33253
33254 Roo.bootstrap.LayoutMasonry = function(config){
33255     
33256     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33257     
33258     this.bricks = [];
33259     
33260     Roo.bootstrap.LayoutMasonry.register(this);
33261     
33262     this.addEvents({
33263         // raw events
33264         /**
33265          * @event layout
33266          * Fire after layout the items
33267          * @param {Roo.bootstrap.LayoutMasonry} this
33268          * @param {Roo.EventObject} e
33269          */
33270         "layout" : true
33271     });
33272     
33273 };
33274
33275 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33276     
33277     /**
33278      * @cfg {Boolean} isLayoutInstant = no animation?
33279      */   
33280     isLayoutInstant : false, // needed?
33281    
33282     /**
33283      * @cfg {Number} boxWidth  width of the columns
33284      */   
33285     boxWidth : 450,
33286     
33287       /**
33288      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33289      */   
33290     boxHeight : 0,
33291     
33292     /**
33293      * @cfg {Number} padWidth padding below box..
33294      */   
33295     padWidth : 10, 
33296     
33297     /**
33298      * @cfg {Number} gutter gutter width..
33299      */   
33300     gutter : 10,
33301     
33302      /**
33303      * @cfg {Number} maxCols maximum number of columns
33304      */   
33305     
33306     maxCols: 0,
33307     
33308     /**
33309      * @cfg {Boolean} isAutoInitial defalut true
33310      */   
33311     isAutoInitial : true, 
33312     
33313     containerWidth: 0,
33314     
33315     /**
33316      * @cfg {Boolean} isHorizontal defalut false
33317      */   
33318     isHorizontal : false, 
33319
33320     currentSize : null,
33321     
33322     tag: 'div',
33323     
33324     cls: '',
33325     
33326     bricks: null, //CompositeElement
33327     
33328     cols : 1,
33329     
33330     _isLayoutInited : false,
33331     
33332 //    isAlternative : false, // only use for vertical layout...
33333     
33334     /**
33335      * @cfg {Number} alternativePadWidth padding below box..
33336      */   
33337     alternativePadWidth : 50,
33338     
33339     selectedBrick : [],
33340     
33341     getAutoCreate : function(){
33342         
33343         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33344         
33345         var cfg = {
33346             tag: this.tag,
33347             cls: 'blog-masonary-wrapper ' + this.cls,
33348             cn : {
33349                 cls : 'mas-boxes masonary'
33350             }
33351         };
33352         
33353         return cfg;
33354     },
33355     
33356     getChildContainer: function( )
33357     {
33358         if (this.boxesEl) {
33359             return this.boxesEl;
33360         }
33361         
33362         this.boxesEl = this.el.select('.mas-boxes').first();
33363         
33364         return this.boxesEl;
33365     },
33366     
33367     
33368     initEvents : function()
33369     {
33370         var _this = this;
33371         
33372         if(this.isAutoInitial){
33373             Roo.log('hook children rendered');
33374             this.on('childrenrendered', function() {
33375                 Roo.log('children rendered');
33376                 _this.initial();
33377             } ,this);
33378         }
33379     },
33380     
33381     initial : function()
33382     {
33383         this.selectedBrick = [];
33384         
33385         this.currentSize = this.el.getBox(true);
33386         
33387         Roo.EventManager.onWindowResize(this.resize, this); 
33388
33389         if(!this.isAutoInitial){
33390             this.layout();
33391             return;
33392         }
33393         
33394         this.layout();
33395         
33396         return;
33397         //this.layout.defer(500,this);
33398         
33399     },
33400     
33401     resize : function()
33402     {
33403         var cs = this.el.getBox(true);
33404         
33405         if (
33406                 this.currentSize.width == cs.width && 
33407                 this.currentSize.x == cs.x && 
33408                 this.currentSize.height == cs.height && 
33409                 this.currentSize.y == cs.y 
33410         ) {
33411             Roo.log("no change in with or X or Y");
33412             return;
33413         }
33414         
33415         this.currentSize = cs;
33416         
33417         this.layout();
33418         
33419     },
33420     
33421     layout : function()
33422     {   
33423         this._resetLayout();
33424         
33425         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33426         
33427         this.layoutItems( isInstant );
33428       
33429         this._isLayoutInited = true;
33430         
33431         this.fireEvent('layout', this);
33432         
33433     },
33434     
33435     _resetLayout : function()
33436     {
33437         if(this.isHorizontal){
33438             this.horizontalMeasureColumns();
33439             return;
33440         }
33441         
33442         this.verticalMeasureColumns();
33443         
33444     },
33445     
33446     verticalMeasureColumns : function()
33447     {
33448         this.getContainerWidth();
33449         
33450 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33451 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33452 //            return;
33453 //        }
33454         
33455         var boxWidth = this.boxWidth + this.padWidth;
33456         
33457         if(this.containerWidth < this.boxWidth){
33458             boxWidth = this.containerWidth
33459         }
33460         
33461         var containerWidth = this.containerWidth;
33462         
33463         var cols = Math.floor(containerWidth / boxWidth);
33464         
33465         this.cols = Math.max( cols, 1 );
33466         
33467         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33468         
33469         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33470         
33471         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33472         
33473         this.colWidth = boxWidth + avail - this.padWidth;
33474         
33475         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33476         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33477     },
33478     
33479     horizontalMeasureColumns : function()
33480     {
33481         this.getContainerWidth();
33482         
33483         var boxWidth = this.boxWidth;
33484         
33485         if(this.containerWidth < boxWidth){
33486             boxWidth = this.containerWidth;
33487         }
33488         
33489         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33490         
33491         this.el.setHeight(boxWidth);
33492         
33493     },
33494     
33495     getContainerWidth : function()
33496     {
33497         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33498     },
33499     
33500     layoutItems : function( isInstant )
33501     {
33502         Roo.log(this.bricks);
33503         
33504         var items = Roo.apply([], this.bricks);
33505         
33506         if(this.isHorizontal){
33507             this._horizontalLayoutItems( items , isInstant );
33508             return;
33509         }
33510         
33511 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33512 //            this._verticalAlternativeLayoutItems( items , isInstant );
33513 //            return;
33514 //        }
33515         
33516         this._verticalLayoutItems( items , isInstant );
33517         
33518     },
33519     
33520     _verticalLayoutItems : function ( items , isInstant)
33521     {
33522         if ( !items || !items.length ) {
33523             return;
33524         }
33525         
33526         var standard = [
33527             ['xs', 'xs', 'xs', 'tall'],
33528             ['xs', 'xs', 'tall'],
33529             ['xs', 'xs', 'sm'],
33530             ['xs', 'xs', 'xs'],
33531             ['xs', 'tall'],
33532             ['xs', 'sm'],
33533             ['xs', 'xs'],
33534             ['xs'],
33535             
33536             ['sm', 'xs', 'xs'],
33537             ['sm', 'xs'],
33538             ['sm'],
33539             
33540             ['tall', 'xs', 'xs', 'xs'],
33541             ['tall', 'xs', 'xs'],
33542             ['tall', 'xs'],
33543             ['tall']
33544             
33545         ];
33546         
33547         var queue = [];
33548         
33549         var boxes = [];
33550         
33551         var box = [];
33552         
33553         Roo.each(items, function(item, k){
33554             
33555             switch (item.size) {
33556                 // these layouts take up a full box,
33557                 case 'md' :
33558                 case 'md-left' :
33559                 case 'md-right' :
33560                 case 'wide' :
33561                     
33562                     if(box.length){
33563                         boxes.push(box);
33564                         box = [];
33565                     }
33566                     
33567                     boxes.push([item]);
33568                     
33569                     break;
33570                     
33571                 case 'xs' :
33572                 case 'sm' :
33573                 case 'tall' :
33574                     
33575                     box.push(item);
33576                     
33577                     break;
33578                 default :
33579                     break;
33580                     
33581             }
33582             
33583         }, this);
33584         
33585         if(box.length){
33586             boxes.push(box);
33587             box = [];
33588         }
33589         
33590         var filterPattern = function(box, length)
33591         {
33592             if(!box.length){
33593                 return;
33594             }
33595             
33596             var match = false;
33597             
33598             var pattern = box.slice(0, length);
33599             
33600             var format = [];
33601             
33602             Roo.each(pattern, function(i){
33603                 format.push(i.size);
33604             }, this);
33605             
33606             Roo.each(standard, function(s){
33607                 
33608                 if(String(s) != String(format)){
33609                     return;
33610                 }
33611                 
33612                 match = true;
33613                 return false;
33614                 
33615             }, this);
33616             
33617             if(!match && length == 1){
33618                 return;
33619             }
33620             
33621             if(!match){
33622                 filterPattern(box, length - 1);
33623                 return;
33624             }
33625                 
33626             queue.push(pattern);
33627
33628             box = box.slice(length, box.length);
33629
33630             filterPattern(box, 4);
33631
33632             return;
33633             
33634         }
33635         
33636         Roo.each(boxes, function(box, k){
33637             
33638             if(!box.length){
33639                 return;
33640             }
33641             
33642             if(box.length == 1){
33643                 queue.push(box);
33644                 return;
33645             }
33646             
33647             filterPattern(box, 4);
33648             
33649         }, this);
33650         
33651         this._processVerticalLayoutQueue( queue, isInstant );
33652         
33653     },
33654     
33655 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33656 //    {
33657 //        if ( !items || !items.length ) {
33658 //            return;
33659 //        }
33660 //
33661 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33662 //        
33663 //    },
33664     
33665     _horizontalLayoutItems : function ( items , isInstant)
33666     {
33667         if ( !items || !items.length || items.length < 3) {
33668             return;
33669         }
33670         
33671         items.reverse();
33672         
33673         var eItems = items.slice(0, 3);
33674         
33675         items = items.slice(3, items.length);
33676         
33677         var standard = [
33678             ['xs', 'xs', 'xs', 'wide'],
33679             ['xs', 'xs', 'wide'],
33680             ['xs', 'xs', 'sm'],
33681             ['xs', 'xs', 'xs'],
33682             ['xs', 'wide'],
33683             ['xs', 'sm'],
33684             ['xs', 'xs'],
33685             ['xs'],
33686             
33687             ['sm', 'xs', 'xs'],
33688             ['sm', 'xs'],
33689             ['sm'],
33690             
33691             ['wide', 'xs', 'xs', 'xs'],
33692             ['wide', 'xs', 'xs'],
33693             ['wide', 'xs'],
33694             ['wide'],
33695             
33696             ['wide-thin']
33697         ];
33698         
33699         var queue = [];
33700         
33701         var boxes = [];
33702         
33703         var box = [];
33704         
33705         Roo.each(items, function(item, k){
33706             
33707             switch (item.size) {
33708                 case 'md' :
33709                 case 'md-left' :
33710                 case 'md-right' :
33711                 case 'tall' :
33712                     
33713                     if(box.length){
33714                         boxes.push(box);
33715                         box = [];
33716                     }
33717                     
33718                     boxes.push([item]);
33719                     
33720                     break;
33721                     
33722                 case 'xs' :
33723                 case 'sm' :
33724                 case 'wide' :
33725                 case 'wide-thin' :
33726                     
33727                     box.push(item);
33728                     
33729                     break;
33730                 default :
33731                     break;
33732                     
33733             }
33734             
33735         }, this);
33736         
33737         if(box.length){
33738             boxes.push(box);
33739             box = [];
33740         }
33741         
33742         var filterPattern = function(box, length)
33743         {
33744             if(!box.length){
33745                 return;
33746             }
33747             
33748             var match = false;
33749             
33750             var pattern = box.slice(0, length);
33751             
33752             var format = [];
33753             
33754             Roo.each(pattern, function(i){
33755                 format.push(i.size);
33756             }, this);
33757             
33758             Roo.each(standard, function(s){
33759                 
33760                 if(String(s) != String(format)){
33761                     return;
33762                 }
33763                 
33764                 match = true;
33765                 return false;
33766                 
33767             }, this);
33768             
33769             if(!match && length == 1){
33770                 return;
33771             }
33772             
33773             if(!match){
33774                 filterPattern(box, length - 1);
33775                 return;
33776             }
33777                 
33778             queue.push(pattern);
33779
33780             box = box.slice(length, box.length);
33781
33782             filterPattern(box, 4);
33783
33784             return;
33785             
33786         }
33787         
33788         Roo.each(boxes, function(box, k){
33789             
33790             if(!box.length){
33791                 return;
33792             }
33793             
33794             if(box.length == 1){
33795                 queue.push(box);
33796                 return;
33797             }
33798             
33799             filterPattern(box, 4);
33800             
33801         }, this);
33802         
33803         
33804         var prune = [];
33805         
33806         var pos = this.el.getBox(true);
33807         
33808         var minX = pos.x;
33809         
33810         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33811         
33812         var hit_end = false;
33813         
33814         Roo.each(queue, function(box){
33815             
33816             if(hit_end){
33817                 
33818                 Roo.each(box, function(b){
33819                 
33820                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33821                     b.el.hide();
33822
33823                 }, this);
33824
33825                 return;
33826             }
33827             
33828             var mx = 0;
33829             
33830             Roo.each(box, function(b){
33831                 
33832                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33833                 b.el.show();
33834
33835                 mx = Math.max(mx, b.x);
33836                 
33837             }, this);
33838             
33839             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33840             
33841             if(maxX < minX){
33842                 
33843                 Roo.each(box, function(b){
33844                 
33845                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33846                     b.el.hide();
33847                     
33848                 }, this);
33849                 
33850                 hit_end = true;
33851                 
33852                 return;
33853             }
33854             
33855             prune.push(box);
33856             
33857         }, this);
33858         
33859         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33860     },
33861     
33862     /** Sets position of item in DOM
33863     * @param {Element} item
33864     * @param {Number} x - horizontal position
33865     * @param {Number} y - vertical position
33866     * @param {Boolean} isInstant - disables transitions
33867     */
33868     _processVerticalLayoutQueue : function( queue, isInstant )
33869     {
33870         var pos = this.el.getBox(true);
33871         var x = pos.x;
33872         var y = pos.y;
33873         var maxY = [];
33874         
33875         for (var i = 0; i < this.cols; i++){
33876             maxY[i] = pos.y;
33877         }
33878         
33879         Roo.each(queue, function(box, k){
33880             
33881             var col = k % this.cols;
33882             
33883             Roo.each(box, function(b,kk){
33884                 
33885                 b.el.position('absolute');
33886                 
33887                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33888                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33889                 
33890                 if(b.size == 'md-left' || b.size == 'md-right'){
33891                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33892                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33893                 }
33894                 
33895                 b.el.setWidth(width);
33896                 b.el.setHeight(height);
33897                 // iframe?
33898                 b.el.select('iframe',true).setSize(width,height);
33899                 
33900             }, this);
33901             
33902             for (var i = 0; i < this.cols; i++){
33903                 
33904                 if(maxY[i] < maxY[col]){
33905                     col = i;
33906                     continue;
33907                 }
33908                 
33909                 col = Math.min(col, i);
33910                 
33911             }
33912             
33913             x = pos.x + col * (this.colWidth + this.padWidth);
33914             
33915             y = maxY[col];
33916             
33917             var positions = [];
33918             
33919             switch (box.length){
33920                 case 1 :
33921                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33922                     break;
33923                 case 2 :
33924                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33925                     break;
33926                 case 3 :
33927                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33928                     break;
33929                 case 4 :
33930                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33931                     break;
33932                 default :
33933                     break;
33934             }
33935             
33936             Roo.each(box, function(b,kk){
33937                 
33938                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33939                 
33940                 var sz = b.el.getSize();
33941                 
33942                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33943                 
33944             }, this);
33945             
33946         }, this);
33947         
33948         var mY = 0;
33949         
33950         for (var i = 0; i < this.cols; i++){
33951             mY = Math.max(mY, maxY[i]);
33952         }
33953         
33954         this.el.setHeight(mY - pos.y);
33955         
33956     },
33957     
33958 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33959 //    {
33960 //        var pos = this.el.getBox(true);
33961 //        var x = pos.x;
33962 //        var y = pos.y;
33963 //        var maxX = pos.right;
33964 //        
33965 //        var maxHeight = 0;
33966 //        
33967 //        Roo.each(items, function(item, k){
33968 //            
33969 //            var c = k % 2;
33970 //            
33971 //            item.el.position('absolute');
33972 //                
33973 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33974 //
33975 //            item.el.setWidth(width);
33976 //
33977 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33978 //
33979 //            item.el.setHeight(height);
33980 //            
33981 //            if(c == 0){
33982 //                item.el.setXY([x, y], isInstant ? false : true);
33983 //            } else {
33984 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33985 //            }
33986 //            
33987 //            y = y + height + this.alternativePadWidth;
33988 //            
33989 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33990 //            
33991 //        }, this);
33992 //        
33993 //        this.el.setHeight(maxHeight);
33994 //        
33995 //    },
33996     
33997     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
33998     {
33999         var pos = this.el.getBox(true);
34000         
34001         var minX = pos.x;
34002         var minY = pos.y;
34003         
34004         var maxX = pos.right;
34005         
34006         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34007         
34008         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34009         
34010         Roo.each(queue, function(box, k){
34011             
34012             Roo.each(box, function(b, kk){
34013                 
34014                 b.el.position('absolute');
34015                 
34016                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34017                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34018                 
34019                 if(b.size == 'md-left' || b.size == 'md-right'){
34020                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34021                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34022                 }
34023                 
34024                 b.el.setWidth(width);
34025                 b.el.setHeight(height);
34026                 
34027             }, this);
34028             
34029             if(!box.length){
34030                 return;
34031             }
34032             
34033             var positions = [];
34034             
34035             switch (box.length){
34036                 case 1 :
34037                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34038                     break;
34039                 case 2 :
34040                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34041                     break;
34042                 case 3 :
34043                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34044                     break;
34045                 case 4 :
34046                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34047                     break;
34048                 default :
34049                     break;
34050             }
34051             
34052             Roo.each(box, function(b,kk){
34053                 
34054                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34055                 
34056                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34057                 
34058             }, this);
34059             
34060         }, this);
34061         
34062     },
34063     
34064     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34065     {
34066         Roo.each(eItems, function(b,k){
34067             
34068             b.size = (k == 0) ? 'sm' : 'xs';
34069             b.x = (k == 0) ? 2 : 1;
34070             b.y = (k == 0) ? 2 : 1;
34071             
34072             b.el.position('absolute');
34073             
34074             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34075                 
34076             b.el.setWidth(width);
34077             
34078             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34079             
34080             b.el.setHeight(height);
34081             
34082         }, this);
34083
34084         var positions = [];
34085         
34086         positions.push({
34087             x : maxX - this.unitWidth * 2 - this.gutter,
34088             y : minY
34089         });
34090         
34091         positions.push({
34092             x : maxX - this.unitWidth,
34093             y : minY + (this.unitWidth + this.gutter) * 2
34094         });
34095         
34096         positions.push({
34097             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34098             y : minY
34099         });
34100         
34101         Roo.each(eItems, function(b,k){
34102             
34103             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34104
34105         }, this);
34106         
34107     },
34108     
34109     getVerticalOneBoxColPositions : function(x, y, box)
34110     {
34111         var pos = [];
34112         
34113         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34114         
34115         if(box[0].size == 'md-left'){
34116             rand = 0;
34117         }
34118         
34119         if(box[0].size == 'md-right'){
34120             rand = 1;
34121         }
34122         
34123         pos.push({
34124             x : x + (this.unitWidth + this.gutter) * rand,
34125             y : y
34126         });
34127         
34128         return pos;
34129     },
34130     
34131     getVerticalTwoBoxColPositions : function(x, y, box)
34132     {
34133         var pos = [];
34134         
34135         if(box[0].size == 'xs'){
34136             
34137             pos.push({
34138                 x : x,
34139                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34140             });
34141
34142             pos.push({
34143                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34144                 y : y
34145             });
34146             
34147             return pos;
34148             
34149         }
34150         
34151         pos.push({
34152             x : x,
34153             y : y
34154         });
34155
34156         pos.push({
34157             x : x + (this.unitWidth + this.gutter) * 2,
34158             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34159         });
34160         
34161         return pos;
34162         
34163     },
34164     
34165     getVerticalThreeBoxColPositions : function(x, y, box)
34166     {
34167         var pos = [];
34168         
34169         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34170             
34171             pos.push({
34172                 x : x,
34173                 y : y
34174             });
34175
34176             pos.push({
34177                 x : x + (this.unitWidth + this.gutter) * 1,
34178                 y : y
34179             });
34180             
34181             pos.push({
34182                 x : x + (this.unitWidth + this.gutter) * 2,
34183                 y : y
34184             });
34185             
34186             return pos;
34187             
34188         }
34189         
34190         if(box[0].size == 'xs' && box[1].size == 'xs'){
34191             
34192             pos.push({
34193                 x : x,
34194                 y : y
34195             });
34196
34197             pos.push({
34198                 x : x,
34199                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34200             });
34201             
34202             pos.push({
34203                 x : x + (this.unitWidth + this.gutter) * 1,
34204                 y : y
34205             });
34206             
34207             return pos;
34208             
34209         }
34210         
34211         pos.push({
34212             x : x,
34213             y : y
34214         });
34215
34216         pos.push({
34217             x : x + (this.unitWidth + this.gutter) * 2,
34218             y : y
34219         });
34220
34221         pos.push({
34222             x : x + (this.unitWidth + this.gutter) * 2,
34223             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34224         });
34225             
34226         return pos;
34227         
34228     },
34229     
34230     getVerticalFourBoxColPositions : function(x, y, box)
34231     {
34232         var pos = [];
34233         
34234         if(box[0].size == 'xs'){
34235             
34236             pos.push({
34237                 x : x,
34238                 y : y
34239             });
34240
34241             pos.push({
34242                 x : x,
34243                 y : y + (this.unitHeight + this.gutter) * 1
34244             });
34245             
34246             pos.push({
34247                 x : x,
34248                 y : y + (this.unitHeight + this.gutter) * 2
34249             });
34250             
34251             pos.push({
34252                 x : x + (this.unitWidth + this.gutter) * 1,
34253                 y : y
34254             });
34255             
34256             return pos;
34257             
34258         }
34259         
34260         pos.push({
34261             x : x,
34262             y : y
34263         });
34264
34265         pos.push({
34266             x : x + (this.unitWidth + this.gutter) * 2,
34267             y : y
34268         });
34269
34270         pos.push({
34271             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34272             y : y + (this.unitHeight + this.gutter) * 1
34273         });
34274
34275         pos.push({
34276             x : x + (this.unitWidth + this.gutter) * 2,
34277             y : y + (this.unitWidth + this.gutter) * 2
34278         });
34279
34280         return pos;
34281         
34282     },
34283     
34284     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34285     {
34286         var pos = [];
34287         
34288         if(box[0].size == 'md-left'){
34289             pos.push({
34290                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34291                 y : minY
34292             });
34293             
34294             return pos;
34295         }
34296         
34297         if(box[0].size == 'md-right'){
34298             pos.push({
34299                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34300                 y : minY + (this.unitWidth + this.gutter) * 1
34301             });
34302             
34303             return pos;
34304         }
34305         
34306         var rand = Math.floor(Math.random() * (4 - box[0].y));
34307         
34308         pos.push({
34309             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34310             y : minY + (this.unitWidth + this.gutter) * rand
34311         });
34312         
34313         return pos;
34314         
34315     },
34316     
34317     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34318     {
34319         var pos = [];
34320         
34321         if(box[0].size == 'xs'){
34322             
34323             pos.push({
34324                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34325                 y : minY
34326             });
34327
34328             pos.push({
34329                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34330                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34331             });
34332             
34333             return pos;
34334             
34335         }
34336         
34337         pos.push({
34338             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34339             y : minY
34340         });
34341
34342         pos.push({
34343             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34344             y : minY + (this.unitWidth + this.gutter) * 2
34345         });
34346         
34347         return pos;
34348         
34349     },
34350     
34351     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34352     {
34353         var pos = [];
34354         
34355         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34356             
34357             pos.push({
34358                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34359                 y : minY
34360             });
34361
34362             pos.push({
34363                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34364                 y : minY + (this.unitWidth + this.gutter) * 1
34365             });
34366             
34367             pos.push({
34368                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34369                 y : minY + (this.unitWidth + this.gutter) * 2
34370             });
34371             
34372             return pos;
34373             
34374         }
34375         
34376         if(box[0].size == 'xs' && box[1].size == 'xs'){
34377             
34378             pos.push({
34379                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34380                 y : minY
34381             });
34382
34383             pos.push({
34384                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34385                 y : minY
34386             });
34387             
34388             pos.push({
34389                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34390                 y : minY + (this.unitWidth + this.gutter) * 1
34391             });
34392             
34393             return pos;
34394             
34395         }
34396         
34397         pos.push({
34398             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34399             y : minY
34400         });
34401
34402         pos.push({
34403             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34404             y : minY + (this.unitWidth + this.gutter) * 2
34405         });
34406
34407         pos.push({
34408             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34409             y : minY + (this.unitWidth + this.gutter) * 2
34410         });
34411             
34412         return pos;
34413         
34414     },
34415     
34416     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34417     {
34418         var pos = [];
34419         
34420         if(box[0].size == 'xs'){
34421             
34422             pos.push({
34423                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34424                 y : minY
34425             });
34426
34427             pos.push({
34428                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34429                 y : minY
34430             });
34431             
34432             pos.push({
34433                 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),
34434                 y : minY
34435             });
34436             
34437             pos.push({
34438                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34439                 y : minY + (this.unitWidth + this.gutter) * 1
34440             });
34441             
34442             return pos;
34443             
34444         }
34445         
34446         pos.push({
34447             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34448             y : minY
34449         });
34450         
34451         pos.push({
34452             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34453             y : minY + (this.unitWidth + this.gutter) * 2
34454         });
34455         
34456         pos.push({
34457             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34458             y : minY + (this.unitWidth + this.gutter) * 2
34459         });
34460         
34461         pos.push({
34462             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),
34463             y : minY + (this.unitWidth + this.gutter) * 2
34464         });
34465
34466         return pos;
34467         
34468     },
34469     
34470     /**
34471     * remove a Masonry Brick
34472     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34473     */
34474     removeBrick : function(brick_id)
34475     {
34476         if (!brick_id) {
34477             return;
34478         }
34479         
34480         for (var i = 0; i<this.bricks.length; i++) {
34481             if (this.bricks[i].id == brick_id) {
34482                 this.bricks.splice(i,1);
34483                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34484                 this.initial();
34485             }
34486         }
34487     },
34488     
34489     /**
34490     * adds a Masonry Brick
34491     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34492     */
34493     addBrick : function(cfg)
34494     {
34495         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34496         //this.register(cn);
34497         cn.parentId = this.id;
34498         cn.render(this.el);
34499         return cn;
34500     },
34501     
34502     /**
34503     * register a Masonry Brick
34504     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34505     */
34506     
34507     register : function(brick)
34508     {
34509         this.bricks.push(brick);
34510         brick.masonryId = this.id;
34511     },
34512     
34513     /**
34514     * clear all the Masonry Brick
34515     */
34516     clearAll : function()
34517     {
34518         this.bricks = [];
34519         //this.getChildContainer().dom.innerHTML = "";
34520         this.el.dom.innerHTML = '';
34521     },
34522     
34523     getSelected : function()
34524     {
34525         if (!this.selectedBrick) {
34526             return false;
34527         }
34528         
34529         return this.selectedBrick;
34530     }
34531 });
34532
34533 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34534     
34535     groups: {},
34536      /**
34537     * register a Masonry Layout
34538     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34539     */
34540     
34541     register : function(layout)
34542     {
34543         this.groups[layout.id] = layout;
34544     },
34545     /**
34546     * fetch a  Masonry Layout based on the masonry layout ID
34547     * @param {string} the masonry layout to add
34548     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34549     */
34550     
34551     get: function(layout_id) {
34552         if (typeof(this.groups[layout_id]) == 'undefined') {
34553             return false;
34554         }
34555         return this.groups[layout_id] ;
34556     }
34557     
34558     
34559     
34560 });
34561
34562  
34563
34564  /**
34565  *
34566  * This is based on 
34567  * http://masonry.desandro.com
34568  *
34569  * The idea is to render all the bricks based on vertical width...
34570  *
34571  * The original code extends 'outlayer' - we might need to use that....
34572  * 
34573  */
34574
34575
34576 /**
34577  * @class Roo.bootstrap.LayoutMasonryAuto
34578  * @extends Roo.bootstrap.Component
34579  * Bootstrap Layout Masonry class
34580  * 
34581  * @constructor
34582  * Create a new Element
34583  * @param {Object} config The config object
34584  */
34585
34586 Roo.bootstrap.LayoutMasonryAuto = function(config){
34587     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34588 };
34589
34590 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34591     
34592       /**
34593      * @cfg {Boolean} isFitWidth  - resize the width..
34594      */   
34595     isFitWidth : false,  // options..
34596     /**
34597      * @cfg {Boolean} isOriginLeft = left align?
34598      */   
34599     isOriginLeft : true,
34600     /**
34601      * @cfg {Boolean} isOriginTop = top align?
34602      */   
34603     isOriginTop : false,
34604     /**
34605      * @cfg {Boolean} isLayoutInstant = no animation?
34606      */   
34607     isLayoutInstant : false, // needed?
34608     /**
34609      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34610      */   
34611     isResizingContainer : true,
34612     /**
34613      * @cfg {Number} columnWidth  width of the columns 
34614      */   
34615     
34616     columnWidth : 0,
34617     
34618     /**
34619      * @cfg {Number} maxCols maximum number of columns
34620      */   
34621     
34622     maxCols: 0,
34623     /**
34624      * @cfg {Number} padHeight padding below box..
34625      */   
34626     
34627     padHeight : 10, 
34628     
34629     /**
34630      * @cfg {Boolean} isAutoInitial defalut true
34631      */   
34632     
34633     isAutoInitial : true, 
34634     
34635     // private?
34636     gutter : 0,
34637     
34638     containerWidth: 0,
34639     initialColumnWidth : 0,
34640     currentSize : null,
34641     
34642     colYs : null, // array.
34643     maxY : 0,
34644     padWidth: 10,
34645     
34646     
34647     tag: 'div',
34648     cls: '',
34649     bricks: null, //CompositeElement
34650     cols : 0, // array?
34651     // element : null, // wrapped now this.el
34652     _isLayoutInited : null, 
34653     
34654     
34655     getAutoCreate : function(){
34656         
34657         var cfg = {
34658             tag: this.tag,
34659             cls: 'blog-masonary-wrapper ' + this.cls,
34660             cn : {
34661                 cls : 'mas-boxes masonary'
34662             }
34663         };
34664         
34665         return cfg;
34666     },
34667     
34668     getChildContainer: function( )
34669     {
34670         if (this.boxesEl) {
34671             return this.boxesEl;
34672         }
34673         
34674         this.boxesEl = this.el.select('.mas-boxes').first();
34675         
34676         return this.boxesEl;
34677     },
34678     
34679     
34680     initEvents : function()
34681     {
34682         var _this = this;
34683         
34684         if(this.isAutoInitial){
34685             Roo.log('hook children rendered');
34686             this.on('childrenrendered', function() {
34687                 Roo.log('children rendered');
34688                 _this.initial();
34689             } ,this);
34690         }
34691         
34692     },
34693     
34694     initial : function()
34695     {
34696         this.reloadItems();
34697
34698         this.currentSize = this.el.getBox(true);
34699
34700         /// was window resize... - let's see if this works..
34701         Roo.EventManager.onWindowResize(this.resize, this); 
34702
34703         if(!this.isAutoInitial){
34704             this.layout();
34705             return;
34706         }
34707         
34708         this.layout.defer(500,this);
34709     },
34710     
34711     reloadItems: function()
34712     {
34713         this.bricks = this.el.select('.masonry-brick', true);
34714         
34715         this.bricks.each(function(b) {
34716             //Roo.log(b.getSize());
34717             if (!b.attr('originalwidth')) {
34718                 b.attr('originalwidth',  b.getSize().width);
34719             }
34720             
34721         });
34722         
34723         Roo.log(this.bricks.elements.length);
34724     },
34725     
34726     resize : function()
34727     {
34728         Roo.log('resize');
34729         var cs = this.el.getBox(true);
34730         
34731         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34732             Roo.log("no change in with or X");
34733             return;
34734         }
34735         this.currentSize = cs;
34736         this.layout();
34737     },
34738     
34739     layout : function()
34740     {
34741          Roo.log('layout');
34742         this._resetLayout();
34743         //this._manageStamps();
34744       
34745         // don't animate first layout
34746         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34747         this.layoutItems( isInstant );
34748       
34749         // flag for initalized
34750         this._isLayoutInited = true;
34751     },
34752     
34753     layoutItems : function( isInstant )
34754     {
34755         //var items = this._getItemsForLayout( this.items );
34756         // original code supports filtering layout items.. we just ignore it..
34757         
34758         this._layoutItems( this.bricks , isInstant );
34759       
34760         this._postLayout();
34761     },
34762     _layoutItems : function ( items , isInstant)
34763     {
34764        //this.fireEvent( 'layout', this, items );
34765     
34766
34767         if ( !items || !items.elements.length ) {
34768           // no items, emit event with empty array
34769             return;
34770         }
34771
34772         var queue = [];
34773         items.each(function(item) {
34774             Roo.log("layout item");
34775             Roo.log(item);
34776             // get x/y object from method
34777             var position = this._getItemLayoutPosition( item );
34778             // enqueue
34779             position.item = item;
34780             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34781             queue.push( position );
34782         }, this);
34783       
34784         this._processLayoutQueue( queue );
34785     },
34786     /** Sets position of item in DOM
34787     * @param {Element} item
34788     * @param {Number} x - horizontal position
34789     * @param {Number} y - vertical position
34790     * @param {Boolean} isInstant - disables transitions
34791     */
34792     _processLayoutQueue : function( queue )
34793     {
34794         for ( var i=0, len = queue.length; i < len; i++ ) {
34795             var obj = queue[i];
34796             obj.item.position('absolute');
34797             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34798         }
34799     },
34800       
34801     
34802     /**
34803     * Any logic you want to do after each layout,
34804     * i.e. size the container
34805     */
34806     _postLayout : function()
34807     {
34808         this.resizeContainer();
34809     },
34810     
34811     resizeContainer : function()
34812     {
34813         if ( !this.isResizingContainer ) {
34814             return;
34815         }
34816         var size = this._getContainerSize();
34817         if ( size ) {
34818             this.el.setSize(size.width,size.height);
34819             this.boxesEl.setSize(size.width,size.height);
34820         }
34821     },
34822     
34823     
34824     
34825     _resetLayout : function()
34826     {
34827         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34828         this.colWidth = this.el.getWidth();
34829         //this.gutter = this.el.getWidth(); 
34830         
34831         this.measureColumns();
34832
34833         // reset column Y
34834         var i = this.cols;
34835         this.colYs = [];
34836         while (i--) {
34837             this.colYs.push( 0 );
34838         }
34839     
34840         this.maxY = 0;
34841     },
34842
34843     measureColumns : function()
34844     {
34845         this.getContainerWidth();
34846       // if columnWidth is 0, default to outerWidth of first item
34847         if ( !this.columnWidth ) {
34848             var firstItem = this.bricks.first();
34849             Roo.log(firstItem);
34850             this.columnWidth  = this.containerWidth;
34851             if (firstItem && firstItem.attr('originalwidth') ) {
34852                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34853             }
34854             // columnWidth fall back to item of first element
34855             Roo.log("set column width?");
34856                         this.initialColumnWidth = this.columnWidth  ;
34857
34858             // if first elem has no width, default to size of container
34859             
34860         }
34861         
34862         
34863         if (this.initialColumnWidth) {
34864             this.columnWidth = this.initialColumnWidth;
34865         }
34866         
34867         
34868             
34869         // column width is fixed at the top - however if container width get's smaller we should
34870         // reduce it...
34871         
34872         // this bit calcs how man columns..
34873             
34874         var columnWidth = this.columnWidth += this.gutter;
34875       
34876         // calculate columns
34877         var containerWidth = this.containerWidth + this.gutter;
34878         
34879         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34880         // fix rounding errors, typically with gutters
34881         var excess = columnWidth - containerWidth % columnWidth;
34882         
34883         
34884         // if overshoot is less than a pixel, round up, otherwise floor it
34885         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34886         cols = Math[ mathMethod ]( cols );
34887         this.cols = Math.max( cols, 1 );
34888         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34889         
34890          // padding positioning..
34891         var totalColWidth = this.cols * this.columnWidth;
34892         var padavail = this.containerWidth - totalColWidth;
34893         // so for 2 columns - we need 3 'pads'
34894         
34895         var padNeeded = (1+this.cols) * this.padWidth;
34896         
34897         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34898         
34899         this.columnWidth += padExtra
34900         //this.padWidth = Math.floor(padavail /  ( this.cols));
34901         
34902         // adjust colum width so that padding is fixed??
34903         
34904         // we have 3 columns ... total = width * 3
34905         // we have X left over... that should be used by 
34906         
34907         //if (this.expandC) {
34908             
34909         //}
34910         
34911         
34912         
34913     },
34914     
34915     getContainerWidth : function()
34916     {
34917        /* // container is parent if fit width
34918         var container = this.isFitWidth ? this.element.parentNode : this.element;
34919         // check that this.size and size are there
34920         // IE8 triggers resize on body size change, so they might not be
34921         
34922         var size = getSize( container );  //FIXME
34923         this.containerWidth = size && size.innerWidth; //FIXME
34924         */
34925          
34926         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34927         
34928     },
34929     
34930     _getItemLayoutPosition : function( item )  // what is item?
34931     {
34932         // we resize the item to our columnWidth..
34933       
34934         item.setWidth(this.columnWidth);
34935         item.autoBoxAdjust  = false;
34936         
34937         var sz = item.getSize();
34938  
34939         // how many columns does this brick span
34940         var remainder = this.containerWidth % this.columnWidth;
34941         
34942         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34943         // round if off by 1 pixel, otherwise use ceil
34944         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34945         colSpan = Math.min( colSpan, this.cols );
34946         
34947         // normally this should be '1' as we dont' currently allow multi width columns..
34948         
34949         var colGroup = this._getColGroup( colSpan );
34950         // get the minimum Y value from the columns
34951         var minimumY = Math.min.apply( Math, colGroup );
34952         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34953         
34954         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34955          
34956         // position the brick
34957         var position = {
34958             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34959             y: this.currentSize.y + minimumY + this.padHeight
34960         };
34961         
34962         Roo.log(position);
34963         // apply setHeight to necessary columns
34964         var setHeight = minimumY + sz.height + this.padHeight;
34965         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34966         
34967         var setSpan = this.cols + 1 - colGroup.length;
34968         for ( var i = 0; i < setSpan; i++ ) {
34969           this.colYs[ shortColIndex + i ] = setHeight ;
34970         }
34971       
34972         return position;
34973     },
34974     
34975     /**
34976      * @param {Number} colSpan - number of columns the element spans
34977      * @returns {Array} colGroup
34978      */
34979     _getColGroup : function( colSpan )
34980     {
34981         if ( colSpan < 2 ) {
34982           // if brick spans only one column, use all the column Ys
34983           return this.colYs;
34984         }
34985       
34986         var colGroup = [];
34987         // how many different places could this brick fit horizontally
34988         var groupCount = this.cols + 1 - colSpan;
34989         // for each group potential horizontal position
34990         for ( var i = 0; i < groupCount; i++ ) {
34991           // make an array of colY values for that one group
34992           var groupColYs = this.colYs.slice( i, i + colSpan );
34993           // and get the max value of the array
34994           colGroup[i] = Math.max.apply( Math, groupColYs );
34995         }
34996         return colGroup;
34997     },
34998     /*
34999     _manageStamp : function( stamp )
35000     {
35001         var stampSize =  stamp.getSize();
35002         var offset = stamp.getBox();
35003         // get the columns that this stamp affects
35004         var firstX = this.isOriginLeft ? offset.x : offset.right;
35005         var lastX = firstX + stampSize.width;
35006         var firstCol = Math.floor( firstX / this.columnWidth );
35007         firstCol = Math.max( 0, firstCol );
35008         
35009         var lastCol = Math.floor( lastX / this.columnWidth );
35010         // lastCol should not go over if multiple of columnWidth #425
35011         lastCol -= lastX % this.columnWidth ? 0 : 1;
35012         lastCol = Math.min( this.cols - 1, lastCol );
35013         
35014         // set colYs to bottom of the stamp
35015         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35016             stampSize.height;
35017             
35018         for ( var i = firstCol; i <= lastCol; i++ ) {
35019           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35020         }
35021     },
35022     */
35023     
35024     _getContainerSize : function()
35025     {
35026         this.maxY = Math.max.apply( Math, this.colYs );
35027         var size = {
35028             height: this.maxY
35029         };
35030       
35031         if ( this.isFitWidth ) {
35032             size.width = this._getContainerFitWidth();
35033         }
35034       
35035         return size;
35036     },
35037     
35038     _getContainerFitWidth : function()
35039     {
35040         var unusedCols = 0;
35041         // count unused columns
35042         var i = this.cols;
35043         while ( --i ) {
35044           if ( this.colYs[i] !== 0 ) {
35045             break;
35046           }
35047           unusedCols++;
35048         }
35049         // fit container to columns that have been used
35050         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35051     },
35052     
35053     needsResizeLayout : function()
35054     {
35055         var previousWidth = this.containerWidth;
35056         this.getContainerWidth();
35057         return previousWidth !== this.containerWidth;
35058     }
35059  
35060 });
35061
35062  
35063
35064  /*
35065  * - LGPL
35066  *
35067  * element
35068  * 
35069  */
35070
35071 /**
35072  * @class Roo.bootstrap.MasonryBrick
35073  * @extends Roo.bootstrap.Component
35074  * Bootstrap MasonryBrick class
35075  * 
35076  * @constructor
35077  * Create a new MasonryBrick
35078  * @param {Object} config The config object
35079  */
35080
35081 Roo.bootstrap.MasonryBrick = function(config){
35082     
35083     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35084     
35085     Roo.bootstrap.MasonryBrick.register(this);
35086     
35087     this.addEvents({
35088         // raw events
35089         /**
35090          * @event click
35091          * When a MasonryBrick is clcik
35092          * @param {Roo.bootstrap.MasonryBrick} this
35093          * @param {Roo.EventObject} e
35094          */
35095         "click" : true
35096     });
35097 };
35098
35099 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35100     
35101     /**
35102      * @cfg {String} title
35103      */   
35104     title : '',
35105     /**
35106      * @cfg {String} html
35107      */   
35108     html : '',
35109     /**
35110      * @cfg {String} bgimage
35111      */   
35112     bgimage : '',
35113     /**
35114      * @cfg {String} videourl
35115      */   
35116     videourl : '',
35117     /**
35118      * @cfg {String} cls
35119      */   
35120     cls : '',
35121     /**
35122      * @cfg {String} href
35123      */   
35124     href : '',
35125     /**
35126      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35127      */   
35128     size : 'xs',
35129     
35130     /**
35131      * @cfg {String} placetitle (center|bottom)
35132      */   
35133     placetitle : '',
35134     
35135     /**
35136      * @cfg {Boolean} isFitContainer defalut true
35137      */   
35138     isFitContainer : true, 
35139     
35140     /**
35141      * @cfg {Boolean} preventDefault defalut false
35142      */   
35143     preventDefault : false, 
35144     
35145     /**
35146      * @cfg {Boolean} inverse defalut false
35147      */   
35148     maskInverse : false, 
35149     
35150     getAutoCreate : function()
35151     {
35152         if(!this.isFitContainer){
35153             return this.getSplitAutoCreate();
35154         }
35155         
35156         var cls = 'masonry-brick masonry-brick-full';
35157         
35158         if(this.href.length){
35159             cls += ' masonry-brick-link';
35160         }
35161         
35162         if(this.bgimage.length){
35163             cls += ' masonry-brick-image';
35164         }
35165         
35166         if(this.maskInverse){
35167             cls += ' mask-inverse';
35168         }
35169         
35170         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35171             cls += ' enable-mask';
35172         }
35173         
35174         if(this.size){
35175             cls += ' masonry-' + this.size + '-brick';
35176         }
35177         
35178         if(this.placetitle.length){
35179             
35180             switch (this.placetitle) {
35181                 case 'center' :
35182                     cls += ' masonry-center-title';
35183                     break;
35184                 case 'bottom' :
35185                     cls += ' masonry-bottom-title';
35186                     break;
35187                 default:
35188                     break;
35189             }
35190             
35191         } else {
35192             if(!this.html.length && !this.bgimage.length){
35193                 cls += ' masonry-center-title';
35194             }
35195
35196             if(!this.html.length && this.bgimage.length){
35197                 cls += ' masonry-bottom-title';
35198             }
35199         }
35200         
35201         if(this.cls){
35202             cls += ' ' + this.cls;
35203         }
35204         
35205         var cfg = {
35206             tag: (this.href.length) ? 'a' : 'div',
35207             cls: cls,
35208             cn: [
35209                 {
35210                     tag: 'div',
35211                     cls: 'masonry-brick-mask'
35212                 },
35213                 {
35214                     tag: 'div',
35215                     cls: 'masonry-brick-paragraph',
35216                     cn: []
35217                 }
35218             ]
35219         };
35220         
35221         if(this.href.length){
35222             cfg.href = this.href;
35223         }
35224         
35225         var cn = cfg.cn[1].cn;
35226         
35227         if(this.title.length){
35228             cn.push({
35229                 tag: 'h4',
35230                 cls: 'masonry-brick-title',
35231                 html: this.title
35232             });
35233         }
35234         
35235         if(this.html.length){
35236             cn.push({
35237                 tag: 'p',
35238                 cls: 'masonry-brick-text',
35239                 html: this.html
35240             });
35241         }
35242         
35243         if (!this.title.length && !this.html.length) {
35244             cfg.cn[1].cls += ' hide';
35245         }
35246         
35247         if(this.bgimage.length){
35248             cfg.cn.push({
35249                 tag: 'img',
35250                 cls: 'masonry-brick-image-view',
35251                 src: this.bgimage
35252             });
35253         }
35254         
35255         if(this.videourl.length){
35256             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35257             // youtube support only?
35258             cfg.cn.push({
35259                 tag: 'iframe',
35260                 cls: 'masonry-brick-image-view',
35261                 src: vurl,
35262                 frameborder : 0,
35263                 allowfullscreen : true
35264             });
35265         }
35266         
35267         return cfg;
35268         
35269     },
35270     
35271     getSplitAutoCreate : function()
35272     {
35273         var cls = 'masonry-brick masonry-brick-split';
35274         
35275         if(this.href.length){
35276             cls += ' masonry-brick-link';
35277         }
35278         
35279         if(this.bgimage.length){
35280             cls += ' masonry-brick-image';
35281         }
35282         
35283         if(this.size){
35284             cls += ' masonry-' + this.size + '-brick';
35285         }
35286         
35287         switch (this.placetitle) {
35288             case 'center' :
35289                 cls += ' masonry-center-title';
35290                 break;
35291             case 'bottom' :
35292                 cls += ' masonry-bottom-title';
35293                 break;
35294             default:
35295                 if(!this.bgimage.length){
35296                     cls += ' masonry-center-title';
35297                 }
35298
35299                 if(this.bgimage.length){
35300                     cls += ' masonry-bottom-title';
35301                 }
35302                 break;
35303         }
35304         
35305         if(this.cls){
35306             cls += ' ' + this.cls;
35307         }
35308         
35309         var cfg = {
35310             tag: (this.href.length) ? 'a' : 'div',
35311             cls: cls,
35312             cn: [
35313                 {
35314                     tag: 'div',
35315                     cls: 'masonry-brick-split-head',
35316                     cn: [
35317                         {
35318                             tag: 'div',
35319                             cls: 'masonry-brick-paragraph',
35320                             cn: []
35321                         }
35322                     ]
35323                 },
35324                 {
35325                     tag: 'div',
35326                     cls: 'masonry-brick-split-body',
35327                     cn: []
35328                 }
35329             ]
35330         };
35331         
35332         if(this.href.length){
35333             cfg.href = this.href;
35334         }
35335         
35336         if(this.title.length){
35337             cfg.cn[0].cn[0].cn.push({
35338                 tag: 'h4',
35339                 cls: 'masonry-brick-title',
35340                 html: this.title
35341             });
35342         }
35343         
35344         if(this.html.length){
35345             cfg.cn[1].cn.push({
35346                 tag: 'p',
35347                 cls: 'masonry-brick-text',
35348                 html: this.html
35349             });
35350         }
35351
35352         if(this.bgimage.length){
35353             cfg.cn[0].cn.push({
35354                 tag: 'img',
35355                 cls: 'masonry-brick-image-view',
35356                 src: this.bgimage
35357             });
35358         }
35359         
35360         if(this.videourl.length){
35361             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35362             // youtube support only?
35363             cfg.cn[0].cn.cn.push({
35364                 tag: 'iframe',
35365                 cls: 'masonry-brick-image-view',
35366                 src: vurl,
35367                 frameborder : 0,
35368                 allowfullscreen : true
35369             });
35370         }
35371         
35372         return cfg;
35373     },
35374     
35375     initEvents: function() 
35376     {
35377         switch (this.size) {
35378             case 'xs' :
35379                 this.x = 1;
35380                 this.y = 1;
35381                 break;
35382             case 'sm' :
35383                 this.x = 2;
35384                 this.y = 2;
35385                 break;
35386             case 'md' :
35387             case 'md-left' :
35388             case 'md-right' :
35389                 this.x = 3;
35390                 this.y = 3;
35391                 break;
35392             case 'tall' :
35393                 this.x = 2;
35394                 this.y = 3;
35395                 break;
35396             case 'wide' :
35397                 this.x = 3;
35398                 this.y = 2;
35399                 break;
35400             case 'wide-thin' :
35401                 this.x = 3;
35402                 this.y = 1;
35403                 break;
35404                         
35405             default :
35406                 break;
35407         }
35408         
35409         if(Roo.isTouch){
35410             this.el.on('touchstart', this.onTouchStart, this);
35411             this.el.on('touchmove', this.onTouchMove, this);
35412             this.el.on('touchend', this.onTouchEnd, this);
35413             this.el.on('contextmenu', this.onContextMenu, this);
35414         } else {
35415             this.el.on('mouseenter'  ,this.enter, this);
35416             this.el.on('mouseleave', this.leave, this);
35417             this.el.on('click', this.onClick, this);
35418         }
35419         
35420         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35421             this.parent().bricks.push(this);   
35422         }
35423         
35424     },
35425     
35426     onClick: function(e, el)
35427     {
35428         var time = this.endTimer - this.startTimer;
35429         // Roo.log(e.preventDefault());
35430         if(Roo.isTouch){
35431             if(time > 1000){
35432                 e.preventDefault();
35433                 return;
35434             }
35435         }
35436         
35437         if(!this.preventDefault){
35438             return;
35439         }
35440         
35441         e.preventDefault();
35442         
35443         if (this.activeClass != '') {
35444             this.selectBrick();
35445         }
35446         
35447         this.fireEvent('click', this, e);
35448     },
35449     
35450     enter: function(e, el)
35451     {
35452         e.preventDefault();
35453         
35454         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35455             return;
35456         }
35457         
35458         if(this.bgimage.length && this.html.length){
35459             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35460         }
35461     },
35462     
35463     leave: function(e, el)
35464     {
35465         e.preventDefault();
35466         
35467         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35468             return;
35469         }
35470         
35471         if(this.bgimage.length && this.html.length){
35472             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35473         }
35474     },
35475     
35476     onTouchStart: function(e, el)
35477     {
35478 //        e.preventDefault();
35479         
35480         this.touchmoved = false;
35481         
35482         if(!this.isFitContainer){
35483             return;
35484         }
35485         
35486         if(!this.bgimage.length || !this.html.length){
35487             return;
35488         }
35489         
35490         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35491         
35492         this.timer = new Date().getTime();
35493         
35494     },
35495     
35496     onTouchMove: function(e, el)
35497     {
35498         this.touchmoved = true;
35499     },
35500     
35501     onContextMenu : function(e,el)
35502     {
35503         e.preventDefault();
35504         e.stopPropagation();
35505         return false;
35506     },
35507     
35508     onTouchEnd: function(e, el)
35509     {
35510 //        e.preventDefault();
35511         
35512         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35513         
35514             this.leave(e,el);
35515             
35516             return;
35517         }
35518         
35519         if(!this.bgimage.length || !this.html.length){
35520             
35521             if(this.href.length){
35522                 window.location.href = this.href;
35523             }
35524             
35525             return;
35526         }
35527         
35528         if(!this.isFitContainer){
35529             return;
35530         }
35531         
35532         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35533         
35534         window.location.href = this.href;
35535     },
35536     
35537     //selection on single brick only
35538     selectBrick : function() {
35539         
35540         if (!this.parentId) {
35541             return;
35542         }
35543         
35544         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35545         var index = m.selectedBrick.indexOf(this.id);
35546         
35547         if ( index > -1) {
35548             m.selectedBrick.splice(index,1);
35549             this.el.removeClass(this.activeClass);
35550             return;
35551         }
35552         
35553         for(var i = 0; i < m.selectedBrick.length; i++) {
35554             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35555             b.el.removeClass(b.activeClass);
35556         }
35557         
35558         m.selectedBrick = [];
35559         
35560         m.selectedBrick.push(this.id);
35561         this.el.addClass(this.activeClass);
35562         return;
35563     },
35564     
35565     isSelected : function(){
35566         return this.el.hasClass(this.activeClass);
35567         
35568     }
35569 });
35570
35571 Roo.apply(Roo.bootstrap.MasonryBrick, {
35572     
35573     //groups: {},
35574     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35575      /**
35576     * register a Masonry Brick
35577     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35578     */
35579     
35580     register : function(brick)
35581     {
35582         //this.groups[brick.id] = brick;
35583         this.groups.add(brick.id, brick);
35584     },
35585     /**
35586     * fetch a  masonry brick based on the masonry brick ID
35587     * @param {string} the masonry brick to add
35588     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35589     */
35590     
35591     get: function(brick_id) 
35592     {
35593         // if (typeof(this.groups[brick_id]) == 'undefined') {
35594         //     return false;
35595         // }
35596         // return this.groups[brick_id] ;
35597         
35598         if(this.groups.key(brick_id)) {
35599             return this.groups.key(brick_id);
35600         }
35601         
35602         return false;
35603     }
35604     
35605     
35606     
35607 });
35608
35609  /*
35610  * - LGPL
35611  *
35612  * element
35613  * 
35614  */
35615
35616 /**
35617  * @class Roo.bootstrap.Brick
35618  * @extends Roo.bootstrap.Component
35619  * Bootstrap Brick class
35620  * 
35621  * @constructor
35622  * Create a new Brick
35623  * @param {Object} config The config object
35624  */
35625
35626 Roo.bootstrap.Brick = function(config){
35627     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35628     
35629     this.addEvents({
35630         // raw events
35631         /**
35632          * @event click
35633          * When a Brick is click
35634          * @param {Roo.bootstrap.Brick} this
35635          * @param {Roo.EventObject} e
35636          */
35637         "click" : true
35638     });
35639 };
35640
35641 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35642     
35643     /**
35644      * @cfg {String} title
35645      */   
35646     title : '',
35647     /**
35648      * @cfg {String} html
35649      */   
35650     html : '',
35651     /**
35652      * @cfg {String} bgimage
35653      */   
35654     bgimage : '',
35655     /**
35656      * @cfg {String} cls
35657      */   
35658     cls : '',
35659     /**
35660      * @cfg {String} href
35661      */   
35662     href : '',
35663     /**
35664      * @cfg {String} video
35665      */   
35666     video : '',
35667     /**
35668      * @cfg {Boolean} square
35669      */   
35670     square : true,
35671     
35672     getAutoCreate : function()
35673     {
35674         var cls = 'roo-brick';
35675         
35676         if(this.href.length){
35677             cls += ' roo-brick-link';
35678         }
35679         
35680         if(this.bgimage.length){
35681             cls += ' roo-brick-image';
35682         }
35683         
35684         if(!this.html.length && !this.bgimage.length){
35685             cls += ' roo-brick-center-title';
35686         }
35687         
35688         if(!this.html.length && this.bgimage.length){
35689             cls += ' roo-brick-bottom-title';
35690         }
35691         
35692         if(this.cls){
35693             cls += ' ' + this.cls;
35694         }
35695         
35696         var cfg = {
35697             tag: (this.href.length) ? 'a' : 'div',
35698             cls: cls,
35699             cn: [
35700                 {
35701                     tag: 'div',
35702                     cls: 'roo-brick-paragraph',
35703                     cn: []
35704                 }
35705             ]
35706         };
35707         
35708         if(this.href.length){
35709             cfg.href = this.href;
35710         }
35711         
35712         var cn = cfg.cn[0].cn;
35713         
35714         if(this.title.length){
35715             cn.push({
35716                 tag: 'h4',
35717                 cls: 'roo-brick-title',
35718                 html: this.title
35719             });
35720         }
35721         
35722         if(this.html.length){
35723             cn.push({
35724                 tag: 'p',
35725                 cls: 'roo-brick-text',
35726                 html: this.html
35727             });
35728         } else {
35729             cn.cls += ' hide';
35730         }
35731         
35732         if(this.bgimage.length){
35733             cfg.cn.push({
35734                 tag: 'img',
35735                 cls: 'roo-brick-image-view',
35736                 src: this.bgimage
35737             });
35738         }
35739         
35740         return cfg;
35741     },
35742     
35743     initEvents: function() 
35744     {
35745         if(this.title.length || this.html.length){
35746             this.el.on('mouseenter'  ,this.enter, this);
35747             this.el.on('mouseleave', this.leave, this);
35748         }
35749         
35750         Roo.EventManager.onWindowResize(this.resize, this); 
35751         
35752         if(this.bgimage.length){
35753             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35754             this.imageEl.on('load', this.onImageLoad, this);
35755             return;
35756         }
35757         
35758         this.resize();
35759     },
35760     
35761     onImageLoad : function()
35762     {
35763         this.resize();
35764     },
35765     
35766     resize : function()
35767     {
35768         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35769         
35770         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35771         
35772         if(this.bgimage.length){
35773             var image = this.el.select('.roo-brick-image-view', true).first();
35774             
35775             image.setWidth(paragraph.getWidth());
35776             
35777             if(this.square){
35778                 image.setHeight(paragraph.getWidth());
35779             }
35780             
35781             this.el.setHeight(image.getHeight());
35782             paragraph.setHeight(image.getHeight());
35783             
35784         }
35785         
35786     },
35787     
35788     enter: function(e, el)
35789     {
35790         e.preventDefault();
35791         
35792         if(this.bgimage.length){
35793             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35794             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35795         }
35796     },
35797     
35798     leave: function(e, el)
35799     {
35800         e.preventDefault();
35801         
35802         if(this.bgimage.length){
35803             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35804             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35805         }
35806     }
35807     
35808 });
35809
35810  
35811
35812  /*
35813  * - LGPL
35814  *
35815  * Number field 
35816  */
35817
35818 /**
35819  * @class Roo.bootstrap.NumberField
35820  * @extends Roo.bootstrap.Input
35821  * Bootstrap NumberField class
35822  * 
35823  * 
35824  * 
35825  * 
35826  * @constructor
35827  * Create a new NumberField
35828  * @param {Object} config The config object
35829  */
35830
35831 Roo.bootstrap.NumberField = function(config){
35832     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35833 };
35834
35835 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35836     
35837     /**
35838      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35839      */
35840     allowDecimals : true,
35841     /**
35842      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35843      */
35844     decimalSeparator : ".",
35845     /**
35846      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35847      */
35848     decimalPrecision : 2,
35849     /**
35850      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35851      */
35852     allowNegative : true,
35853     
35854     /**
35855      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35856      */
35857     allowZero: true,
35858     /**
35859      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35860      */
35861     minValue : Number.NEGATIVE_INFINITY,
35862     /**
35863      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35864      */
35865     maxValue : Number.MAX_VALUE,
35866     /**
35867      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35868      */
35869     minText : "The minimum value for this field is {0}",
35870     /**
35871      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35872      */
35873     maxText : "The maximum value for this field is {0}",
35874     /**
35875      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35876      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35877      */
35878     nanText : "{0} is not a valid number",
35879     /**
35880      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35881      */
35882     thousandsDelimiter : false,
35883     /**
35884      * @cfg {String} valueAlign alignment of value
35885      */
35886     valueAlign : "left",
35887
35888     getAutoCreate : function()
35889     {
35890         var hiddenInput = {
35891             tag: 'input',
35892             type: 'hidden',
35893             id: Roo.id(),
35894             cls: 'hidden-number-input'
35895         };
35896         
35897         if (this.name) {
35898             hiddenInput.name = this.name;
35899         }
35900         
35901         this.name = '';
35902         
35903         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35904         
35905         this.name = hiddenInput.name;
35906         
35907         if(cfg.cn.length > 0) {
35908             cfg.cn.push(hiddenInput);
35909         }
35910         
35911         return cfg;
35912     },
35913
35914     // private
35915     initEvents : function()
35916     {   
35917         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35918         
35919         var allowed = "0123456789";
35920         
35921         if(this.allowDecimals){
35922             allowed += this.decimalSeparator;
35923         }
35924         
35925         if(this.allowNegative){
35926             allowed += "-";
35927         }
35928         
35929         if(this.thousandsDelimiter) {
35930             allowed += ",";
35931         }
35932         
35933         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35934         
35935         var keyPress = function(e){
35936             
35937             var k = e.getKey();
35938             
35939             var c = e.getCharCode();
35940             
35941             if(
35942                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35943                     allowed.indexOf(String.fromCharCode(c)) === -1
35944             ){
35945                 e.stopEvent();
35946                 return;
35947             }
35948             
35949             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35950                 return;
35951             }
35952             
35953             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35954                 e.stopEvent();
35955             }
35956         };
35957         
35958         this.el.on("keypress", keyPress, this);
35959     },
35960     
35961     validateValue : function(value)
35962     {
35963         
35964         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35965             return false;
35966         }
35967         
35968         var num = this.parseValue(value);
35969         
35970         if(isNaN(num)){
35971             this.markInvalid(String.format(this.nanText, value));
35972             return false;
35973         }
35974         
35975         if(num < this.minValue){
35976             this.markInvalid(String.format(this.minText, this.minValue));
35977             return false;
35978         }
35979         
35980         if(num > this.maxValue){
35981             this.markInvalid(String.format(this.maxText, this.maxValue));
35982             return false;
35983         }
35984         
35985         return true;
35986     },
35987
35988     getValue : function()
35989     {
35990         var v = this.hiddenEl().getValue();
35991         
35992         return this.fixPrecision(this.parseValue(v));
35993     },
35994
35995     parseValue : function(value)
35996     {
35997         if(this.thousandsDelimiter) {
35998             value += "";
35999             r = new RegExp(",", "g");
36000             value = value.replace(r, "");
36001         }
36002         
36003         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36004         return isNaN(value) ? '' : value;
36005     },
36006
36007     fixPrecision : function(value)
36008     {
36009         if(this.thousandsDelimiter) {
36010             value += "";
36011             r = new RegExp(",", "g");
36012             value = value.replace(r, "");
36013         }
36014         
36015         var nan = isNaN(value);
36016         
36017         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36018             return nan ? '' : value;
36019         }
36020         return parseFloat(value).toFixed(this.decimalPrecision);
36021     },
36022
36023     setValue : function(v)
36024     {
36025         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36026         
36027         this.value = v;
36028         
36029         if(this.rendered){
36030             
36031             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36032             
36033             this.inputEl().dom.value = (v == '') ? '' :
36034                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36035             
36036             if(!this.allowZero && v === '0') {
36037                 this.hiddenEl().dom.value = '';
36038                 this.inputEl().dom.value = '';
36039             }
36040             
36041             this.validate();
36042         }
36043     },
36044
36045     decimalPrecisionFcn : function(v)
36046     {
36047         return Math.floor(v);
36048     },
36049
36050     beforeBlur : function()
36051     {
36052         var v = this.parseValue(this.getRawValue());
36053         
36054         if(v || v === 0 || v === ''){
36055             this.setValue(v);
36056         }
36057     },
36058     
36059     hiddenEl : function()
36060     {
36061         return this.el.select('input.hidden-number-input',true).first();
36062     }
36063     
36064 });
36065
36066  
36067
36068 /*
36069 * Licence: LGPL
36070 */
36071
36072 /**
36073  * @class Roo.bootstrap.DocumentSlider
36074  * @extends Roo.bootstrap.Component
36075  * Bootstrap DocumentSlider class
36076  * 
36077  * @constructor
36078  * Create a new DocumentViewer
36079  * @param {Object} config The config object
36080  */
36081
36082 Roo.bootstrap.DocumentSlider = function(config){
36083     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36084     
36085     this.files = [];
36086     
36087     this.addEvents({
36088         /**
36089          * @event initial
36090          * Fire after initEvent
36091          * @param {Roo.bootstrap.DocumentSlider} this
36092          */
36093         "initial" : true,
36094         /**
36095          * @event update
36096          * Fire after update
36097          * @param {Roo.bootstrap.DocumentSlider} this
36098          */
36099         "update" : true,
36100         /**
36101          * @event click
36102          * Fire after click
36103          * @param {Roo.bootstrap.DocumentSlider} this
36104          */
36105         "click" : true
36106     });
36107 };
36108
36109 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36110     
36111     files : false,
36112     
36113     indicator : 0,
36114     
36115     getAutoCreate : function()
36116     {
36117         var cfg = {
36118             tag : 'div',
36119             cls : 'roo-document-slider',
36120             cn : [
36121                 {
36122                     tag : 'div',
36123                     cls : 'roo-document-slider-header',
36124                     cn : [
36125                         {
36126                             tag : 'div',
36127                             cls : 'roo-document-slider-header-title'
36128                         }
36129                     ]
36130                 },
36131                 {
36132                     tag : 'div',
36133                     cls : 'roo-document-slider-body',
36134                     cn : [
36135                         {
36136                             tag : 'div',
36137                             cls : 'roo-document-slider-prev',
36138                             cn : [
36139                                 {
36140                                     tag : 'i',
36141                                     cls : 'fa fa-chevron-left'
36142                                 }
36143                             ]
36144                         },
36145                         {
36146                             tag : 'div',
36147                             cls : 'roo-document-slider-thumb',
36148                             cn : [
36149                                 {
36150                                     tag : 'img',
36151                                     cls : 'roo-document-slider-image'
36152                                 }
36153                             ]
36154                         },
36155                         {
36156                             tag : 'div',
36157                             cls : 'roo-document-slider-next',
36158                             cn : [
36159                                 {
36160                                     tag : 'i',
36161                                     cls : 'fa fa-chevron-right'
36162                                 }
36163                             ]
36164                         }
36165                     ]
36166                 }
36167             ]
36168         };
36169         
36170         return cfg;
36171     },
36172     
36173     initEvents : function()
36174     {
36175         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36176         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36177         
36178         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36179         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36180         
36181         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36182         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36183         
36184         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36185         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36186         
36187         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36188         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36189         
36190         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36191         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36192         
36193         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36194         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36195         
36196         this.thumbEl.on('click', this.onClick, this);
36197         
36198         this.prevIndicator.on('click', this.prev, this);
36199         
36200         this.nextIndicator.on('click', this.next, this);
36201         
36202     },
36203     
36204     initial : function()
36205     {
36206         if(this.files.length){
36207             this.indicator = 1;
36208             this.update()
36209         }
36210         
36211         this.fireEvent('initial', this);
36212     },
36213     
36214     update : function()
36215     {
36216         this.imageEl.attr('src', this.files[this.indicator - 1]);
36217         
36218         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36219         
36220         this.prevIndicator.show();
36221         
36222         if(this.indicator == 1){
36223             this.prevIndicator.hide();
36224         }
36225         
36226         this.nextIndicator.show();
36227         
36228         if(this.indicator == this.files.length){
36229             this.nextIndicator.hide();
36230         }
36231         
36232         this.thumbEl.scrollTo('top');
36233         
36234         this.fireEvent('update', this);
36235     },
36236     
36237     onClick : function(e)
36238     {
36239         e.preventDefault();
36240         
36241         this.fireEvent('click', this);
36242     },
36243     
36244     prev : function(e)
36245     {
36246         e.preventDefault();
36247         
36248         this.indicator = Math.max(1, this.indicator - 1);
36249         
36250         this.update();
36251     },
36252     
36253     next : function(e)
36254     {
36255         e.preventDefault();
36256         
36257         this.indicator = Math.min(this.files.length, this.indicator + 1);
36258         
36259         this.update();
36260     }
36261 });
36262 /*
36263  * - LGPL
36264  *
36265  * RadioSet
36266  *
36267  *
36268  */
36269
36270 /**
36271  * @class Roo.bootstrap.RadioSet
36272  * @extends Roo.bootstrap.Input
36273  * Bootstrap RadioSet class
36274  * @cfg {String} indicatorpos (left|right) default left
36275  * @cfg {Boolean} inline (true|false) inline the element (default true)
36276  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36277  * @constructor
36278  * Create a new RadioSet
36279  * @param {Object} config The config object
36280  */
36281
36282 Roo.bootstrap.RadioSet = function(config){
36283     
36284     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36285     
36286     this.radioes = [];
36287     
36288     Roo.bootstrap.RadioSet.register(this);
36289     
36290     this.addEvents({
36291         /**
36292         * @event check
36293         * Fires when the element is checked or unchecked.
36294         * @param {Roo.bootstrap.RadioSet} this This radio
36295         * @param {Roo.bootstrap.Radio} item The checked item
36296         */
36297        check : true,
36298        /**
36299         * @event click
36300         * Fires when the element is click.
36301         * @param {Roo.bootstrap.RadioSet} this This radio set
36302         * @param {Roo.bootstrap.Radio} item The checked item
36303         * @param {Roo.EventObject} e The event object
36304         */
36305        click : true
36306     });
36307     
36308 };
36309
36310 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36311
36312     radioes : false,
36313     
36314     inline : true,
36315     
36316     weight : '',
36317     
36318     indicatorpos : 'left',
36319     
36320     getAutoCreate : function()
36321     {
36322         var label = {
36323             tag : 'label',
36324             cls : 'roo-radio-set-label',
36325             cn : [
36326                 {
36327                     tag : 'span',
36328                     html : this.fieldLabel
36329                 }
36330             ]
36331         };
36332         if (Roo.bootstrap.version == 3) {
36333             
36334             
36335             if(this.indicatorpos == 'left'){
36336                 label.cn.unshift({
36337                     tag : 'i',
36338                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36339                     tooltip : 'This field is required'
36340                 });
36341             } else {
36342                 label.cn.push({
36343                     tag : 'i',
36344                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36345                     tooltip : 'This field is required'
36346                 });
36347             }
36348         }
36349         var items = {
36350             tag : 'div',
36351             cls : 'roo-radio-set-items'
36352         };
36353         
36354         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36355         
36356         if (align === 'left' && this.fieldLabel.length) {
36357             
36358             items = {
36359                 cls : "roo-radio-set-right", 
36360                 cn: [
36361                     items
36362                 ]
36363             };
36364             
36365             if(this.labelWidth > 12){
36366                 label.style = "width: " + this.labelWidth + 'px';
36367             }
36368             
36369             if(this.labelWidth < 13 && this.labelmd == 0){
36370                 this.labelmd = this.labelWidth;
36371             }
36372             
36373             if(this.labellg > 0){
36374                 label.cls += ' col-lg-' + this.labellg;
36375                 items.cls += ' col-lg-' + (12 - this.labellg);
36376             }
36377             
36378             if(this.labelmd > 0){
36379                 label.cls += ' col-md-' + this.labelmd;
36380                 items.cls += ' col-md-' + (12 - this.labelmd);
36381             }
36382             
36383             if(this.labelsm > 0){
36384                 label.cls += ' col-sm-' + this.labelsm;
36385                 items.cls += ' col-sm-' + (12 - this.labelsm);
36386             }
36387             
36388             if(this.labelxs > 0){
36389                 label.cls += ' col-xs-' + this.labelxs;
36390                 items.cls += ' col-xs-' + (12 - this.labelxs);
36391             }
36392         }
36393         
36394         var cfg = {
36395             tag : 'div',
36396             cls : 'roo-radio-set',
36397             cn : [
36398                 {
36399                     tag : 'input',
36400                     cls : 'roo-radio-set-input',
36401                     type : 'hidden',
36402                     name : this.name,
36403                     value : this.value ? this.value :  ''
36404                 },
36405                 label,
36406                 items
36407             ]
36408         };
36409         
36410         if(this.weight.length){
36411             cfg.cls += ' roo-radio-' + this.weight;
36412         }
36413         
36414         if(this.inline) {
36415             cfg.cls += ' roo-radio-set-inline';
36416         }
36417         
36418         var settings=this;
36419         ['xs','sm','md','lg'].map(function(size){
36420             if (settings[size]) {
36421                 cfg.cls += ' col-' + size + '-' + settings[size];
36422             }
36423         });
36424         
36425         return cfg;
36426         
36427     },
36428
36429     initEvents : function()
36430     {
36431         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36432         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36433         
36434         if(!this.fieldLabel.length){
36435             this.labelEl.hide();
36436         }
36437         
36438         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36439         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36440         
36441         this.indicator = this.indicatorEl();
36442         
36443         if(this.indicator){
36444             this.indicator.addClass('invisible');
36445         }
36446         
36447         this.originalValue = this.getValue();
36448         
36449     },
36450     
36451     inputEl: function ()
36452     {
36453         return this.el.select('.roo-radio-set-input', true).first();
36454     },
36455     
36456     getChildContainer : function()
36457     {
36458         return this.itemsEl;
36459     },
36460     
36461     register : function(item)
36462     {
36463         this.radioes.push(item);
36464         
36465     },
36466     
36467     validate : function()
36468     {   
36469         if(this.getVisibilityEl().hasClass('hidden')){
36470             return true;
36471         }
36472         
36473         var valid = false;
36474         
36475         Roo.each(this.radioes, function(i){
36476             if(!i.checked){
36477                 return;
36478             }
36479             
36480             valid = true;
36481             return false;
36482         });
36483         
36484         if(this.allowBlank) {
36485             return true;
36486         }
36487         
36488         if(this.disabled || valid){
36489             this.markValid();
36490             return true;
36491         }
36492         
36493         this.markInvalid();
36494         return false;
36495         
36496     },
36497     
36498     markValid : function()
36499     {
36500         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36501             this.indicatorEl().removeClass('visible');
36502             this.indicatorEl().addClass('invisible');
36503         }
36504         
36505         
36506         if (Roo.bootstrap.version == 3) {
36507             this.el.removeClass([this.invalidClass, this.validClass]);
36508             this.el.addClass(this.validClass);
36509         } else {
36510             this.el.removeClass(['is-invalid','is-valid']);
36511             this.el.addClass(['is-valid']);
36512         }
36513         this.fireEvent('valid', this);
36514     },
36515     
36516     markInvalid : function(msg)
36517     {
36518         if(this.allowBlank || this.disabled){
36519             return;
36520         }
36521         
36522         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36523             this.indicatorEl().removeClass('invisible');
36524             this.indicatorEl().addClass('visible');
36525         }
36526         if (Roo.bootstrap.version == 3) {
36527             this.el.removeClass([this.invalidClass, this.validClass]);
36528             this.el.addClass(this.invalidClass);
36529         } else {
36530             this.el.removeClass(['is-invalid','is-valid']);
36531             this.el.addClass(['is-invalid']);
36532         }
36533         
36534         this.fireEvent('invalid', this, msg);
36535         
36536     },
36537     
36538     setValue : function(v, suppressEvent)
36539     {   
36540         if(this.value === v){
36541             return;
36542         }
36543         
36544         this.value = v;
36545         
36546         if(this.rendered){
36547             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36548         }
36549         
36550         Roo.each(this.radioes, function(i){
36551             i.checked = false;
36552             i.el.removeClass('checked');
36553         });
36554         
36555         Roo.each(this.radioes, function(i){
36556             
36557             if(i.value === v || i.value.toString() === v.toString()){
36558                 i.checked = true;
36559                 i.el.addClass('checked');
36560                 
36561                 if(suppressEvent !== true){
36562                     this.fireEvent('check', this, i);
36563                 }
36564                 
36565                 return false;
36566             }
36567             
36568         }, this);
36569         
36570         this.validate();
36571     },
36572     
36573     clearInvalid : function(){
36574         
36575         if(!this.el || this.preventMark){
36576             return;
36577         }
36578         
36579         this.el.removeClass([this.invalidClass]);
36580         
36581         this.fireEvent('valid', this);
36582     }
36583     
36584 });
36585
36586 Roo.apply(Roo.bootstrap.RadioSet, {
36587     
36588     groups: {},
36589     
36590     register : function(set)
36591     {
36592         this.groups[set.name] = set;
36593     },
36594     
36595     get: function(name) 
36596     {
36597         if (typeof(this.groups[name]) == 'undefined') {
36598             return false;
36599         }
36600         
36601         return this.groups[name] ;
36602     }
36603     
36604 });
36605 /*
36606  * Based on:
36607  * Ext JS Library 1.1.1
36608  * Copyright(c) 2006-2007, Ext JS, LLC.
36609  *
36610  * Originally Released Under LGPL - original licence link has changed is not relivant.
36611  *
36612  * Fork - LGPL
36613  * <script type="text/javascript">
36614  */
36615
36616
36617 /**
36618  * @class Roo.bootstrap.SplitBar
36619  * @extends Roo.util.Observable
36620  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36621  * <br><br>
36622  * Usage:
36623  * <pre><code>
36624 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36625                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36626 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36627 split.minSize = 100;
36628 split.maxSize = 600;
36629 split.animate = true;
36630 split.on('moved', splitterMoved);
36631 </code></pre>
36632  * @constructor
36633  * Create a new SplitBar
36634  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36635  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36636  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36637  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36638                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36639                         position of the SplitBar).
36640  */
36641 Roo.bootstrap.SplitBar = function(cfg){
36642     
36643     /** @private */
36644     
36645     //{
36646     //  dragElement : elm
36647     //  resizingElement: el,
36648         // optional..
36649     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36650     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36651         // existingProxy ???
36652     //}
36653     
36654     this.el = Roo.get(cfg.dragElement, true);
36655     this.el.dom.unselectable = "on";
36656     /** @private */
36657     this.resizingEl = Roo.get(cfg.resizingElement, true);
36658
36659     /**
36660      * @private
36661      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36662      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36663      * @type Number
36664      */
36665     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36666     
36667     /**
36668      * The minimum size of the resizing element. (Defaults to 0)
36669      * @type Number
36670      */
36671     this.minSize = 0;
36672     
36673     /**
36674      * The maximum size of the resizing element. (Defaults to 2000)
36675      * @type Number
36676      */
36677     this.maxSize = 2000;
36678     
36679     /**
36680      * Whether to animate the transition to the new size
36681      * @type Boolean
36682      */
36683     this.animate = false;
36684     
36685     /**
36686      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36687      * @type Boolean
36688      */
36689     this.useShim = false;
36690     
36691     /** @private */
36692     this.shim = null;
36693     
36694     if(!cfg.existingProxy){
36695         /** @private */
36696         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36697     }else{
36698         this.proxy = Roo.get(cfg.existingProxy).dom;
36699     }
36700     /** @private */
36701     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36702     
36703     /** @private */
36704     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36705     
36706     /** @private */
36707     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36708     
36709     /** @private */
36710     this.dragSpecs = {};
36711     
36712     /**
36713      * @private The adapter to use to positon and resize elements
36714      */
36715     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36716     this.adapter.init(this);
36717     
36718     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36719         /** @private */
36720         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36721         this.el.addClass("roo-splitbar-h");
36722     }else{
36723         /** @private */
36724         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36725         this.el.addClass("roo-splitbar-v");
36726     }
36727     
36728     this.addEvents({
36729         /**
36730          * @event resize
36731          * Fires when the splitter is moved (alias for {@link #event-moved})
36732          * @param {Roo.bootstrap.SplitBar} this
36733          * @param {Number} newSize the new width or height
36734          */
36735         "resize" : true,
36736         /**
36737          * @event moved
36738          * Fires when the splitter is moved
36739          * @param {Roo.bootstrap.SplitBar} this
36740          * @param {Number} newSize the new width or height
36741          */
36742         "moved" : true,
36743         /**
36744          * @event beforeresize
36745          * Fires before the splitter is dragged
36746          * @param {Roo.bootstrap.SplitBar} this
36747          */
36748         "beforeresize" : true,
36749
36750         "beforeapply" : true
36751     });
36752
36753     Roo.util.Observable.call(this);
36754 };
36755
36756 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36757     onStartProxyDrag : function(x, y){
36758         this.fireEvent("beforeresize", this);
36759         if(!this.overlay){
36760             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36761             o.unselectable();
36762             o.enableDisplayMode("block");
36763             // all splitbars share the same overlay
36764             Roo.bootstrap.SplitBar.prototype.overlay = o;
36765         }
36766         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36767         this.overlay.show();
36768         Roo.get(this.proxy).setDisplayed("block");
36769         var size = this.adapter.getElementSize(this);
36770         this.activeMinSize = this.getMinimumSize();;
36771         this.activeMaxSize = this.getMaximumSize();;
36772         var c1 = size - this.activeMinSize;
36773         var c2 = Math.max(this.activeMaxSize - size, 0);
36774         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36775             this.dd.resetConstraints();
36776             this.dd.setXConstraint(
36777                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36778                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36779             );
36780             this.dd.setYConstraint(0, 0);
36781         }else{
36782             this.dd.resetConstraints();
36783             this.dd.setXConstraint(0, 0);
36784             this.dd.setYConstraint(
36785                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36786                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36787             );
36788          }
36789         this.dragSpecs.startSize = size;
36790         this.dragSpecs.startPoint = [x, y];
36791         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36792     },
36793     
36794     /** 
36795      * @private Called after the drag operation by the DDProxy
36796      */
36797     onEndProxyDrag : function(e){
36798         Roo.get(this.proxy).setDisplayed(false);
36799         var endPoint = Roo.lib.Event.getXY(e);
36800         if(this.overlay){
36801             this.overlay.hide();
36802         }
36803         var newSize;
36804         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36805             newSize = this.dragSpecs.startSize + 
36806                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36807                     endPoint[0] - this.dragSpecs.startPoint[0] :
36808                     this.dragSpecs.startPoint[0] - endPoint[0]
36809                 );
36810         }else{
36811             newSize = this.dragSpecs.startSize + 
36812                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36813                     endPoint[1] - this.dragSpecs.startPoint[1] :
36814                     this.dragSpecs.startPoint[1] - endPoint[1]
36815                 );
36816         }
36817         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36818         if(newSize != this.dragSpecs.startSize){
36819             if(this.fireEvent('beforeapply', this, newSize) !== false){
36820                 this.adapter.setElementSize(this, newSize);
36821                 this.fireEvent("moved", this, newSize);
36822                 this.fireEvent("resize", this, newSize);
36823             }
36824         }
36825     },
36826     
36827     /**
36828      * Get the adapter this SplitBar uses
36829      * @return The adapter object
36830      */
36831     getAdapter : function(){
36832         return this.adapter;
36833     },
36834     
36835     /**
36836      * Set the adapter this SplitBar uses
36837      * @param {Object} adapter A SplitBar adapter object
36838      */
36839     setAdapter : function(adapter){
36840         this.adapter = adapter;
36841         this.adapter.init(this);
36842     },
36843     
36844     /**
36845      * Gets the minimum size for the resizing element
36846      * @return {Number} The minimum size
36847      */
36848     getMinimumSize : function(){
36849         return this.minSize;
36850     },
36851     
36852     /**
36853      * Sets the minimum size for the resizing element
36854      * @param {Number} minSize The minimum size
36855      */
36856     setMinimumSize : function(minSize){
36857         this.minSize = minSize;
36858     },
36859     
36860     /**
36861      * Gets the maximum size for the resizing element
36862      * @return {Number} The maximum size
36863      */
36864     getMaximumSize : function(){
36865         return this.maxSize;
36866     },
36867     
36868     /**
36869      * Sets the maximum size for the resizing element
36870      * @param {Number} maxSize The maximum size
36871      */
36872     setMaximumSize : function(maxSize){
36873         this.maxSize = maxSize;
36874     },
36875     
36876     /**
36877      * Sets the initialize size for the resizing element
36878      * @param {Number} size The initial size
36879      */
36880     setCurrentSize : function(size){
36881         var oldAnimate = this.animate;
36882         this.animate = false;
36883         this.adapter.setElementSize(this, size);
36884         this.animate = oldAnimate;
36885     },
36886     
36887     /**
36888      * Destroy this splitbar. 
36889      * @param {Boolean} removeEl True to remove the element
36890      */
36891     destroy : function(removeEl){
36892         if(this.shim){
36893             this.shim.remove();
36894         }
36895         this.dd.unreg();
36896         this.proxy.parentNode.removeChild(this.proxy);
36897         if(removeEl){
36898             this.el.remove();
36899         }
36900     }
36901 });
36902
36903 /**
36904  * @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.
36905  */
36906 Roo.bootstrap.SplitBar.createProxy = function(dir){
36907     var proxy = new Roo.Element(document.createElement("div"));
36908     proxy.unselectable();
36909     var cls = 'roo-splitbar-proxy';
36910     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36911     document.body.appendChild(proxy.dom);
36912     return proxy.dom;
36913 };
36914
36915 /** 
36916  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36917  * Default Adapter. It assumes the splitter and resizing element are not positioned
36918  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36919  */
36920 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36921 };
36922
36923 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36924     // do nothing for now
36925     init : function(s){
36926     
36927     },
36928     /**
36929      * Called before drag operations to get the current size of the resizing element. 
36930      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36931      */
36932      getElementSize : function(s){
36933         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36934             return s.resizingEl.getWidth();
36935         }else{
36936             return s.resizingEl.getHeight();
36937         }
36938     },
36939     
36940     /**
36941      * Called after drag operations to set the size of the resizing element.
36942      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36943      * @param {Number} newSize The new size to set
36944      * @param {Function} onComplete A function to be invoked when resizing is complete
36945      */
36946     setElementSize : function(s, newSize, onComplete){
36947         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36948             if(!s.animate){
36949                 s.resizingEl.setWidth(newSize);
36950                 if(onComplete){
36951                     onComplete(s, newSize);
36952                 }
36953             }else{
36954                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36955             }
36956         }else{
36957             
36958             if(!s.animate){
36959                 s.resizingEl.setHeight(newSize);
36960                 if(onComplete){
36961                     onComplete(s, newSize);
36962                 }
36963             }else{
36964                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36965             }
36966         }
36967     }
36968 };
36969
36970 /** 
36971  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36972  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36973  * Adapter that  moves the splitter element to align with the resized sizing element. 
36974  * Used with an absolute positioned SplitBar.
36975  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36976  * document.body, make sure you assign an id to the body element.
36977  */
36978 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36979     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36980     this.container = Roo.get(container);
36981 };
36982
36983 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36984     init : function(s){
36985         this.basic.init(s);
36986     },
36987     
36988     getElementSize : function(s){
36989         return this.basic.getElementSize(s);
36990     },
36991     
36992     setElementSize : function(s, newSize, onComplete){
36993         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
36994     },
36995     
36996     moveSplitter : function(s){
36997         var yes = Roo.bootstrap.SplitBar;
36998         switch(s.placement){
36999             case yes.LEFT:
37000                 s.el.setX(s.resizingEl.getRight());
37001                 break;
37002             case yes.RIGHT:
37003                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37004                 break;
37005             case yes.TOP:
37006                 s.el.setY(s.resizingEl.getBottom());
37007                 break;
37008             case yes.BOTTOM:
37009                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37010                 break;
37011         }
37012     }
37013 };
37014
37015 /**
37016  * Orientation constant - Create a vertical SplitBar
37017  * @static
37018  * @type Number
37019  */
37020 Roo.bootstrap.SplitBar.VERTICAL = 1;
37021
37022 /**
37023  * Orientation constant - Create a horizontal SplitBar
37024  * @static
37025  * @type Number
37026  */
37027 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37028
37029 /**
37030  * Placement constant - The resizing element is to the left of the splitter element
37031  * @static
37032  * @type Number
37033  */
37034 Roo.bootstrap.SplitBar.LEFT = 1;
37035
37036 /**
37037  * Placement constant - The resizing element is to the right of the splitter element
37038  * @static
37039  * @type Number
37040  */
37041 Roo.bootstrap.SplitBar.RIGHT = 2;
37042
37043 /**
37044  * Placement constant - The resizing element is positioned above the splitter element
37045  * @static
37046  * @type Number
37047  */
37048 Roo.bootstrap.SplitBar.TOP = 3;
37049
37050 /**
37051  * Placement constant - The resizing element is positioned under splitter element
37052  * @static
37053  * @type Number
37054  */
37055 Roo.bootstrap.SplitBar.BOTTOM = 4;
37056 Roo.namespace("Roo.bootstrap.layout");/*
37057  * Based on:
37058  * Ext JS Library 1.1.1
37059  * Copyright(c) 2006-2007, Ext JS, LLC.
37060  *
37061  * Originally Released Under LGPL - original licence link has changed is not relivant.
37062  *
37063  * Fork - LGPL
37064  * <script type="text/javascript">
37065  */
37066
37067 /**
37068  * @class Roo.bootstrap.layout.Manager
37069  * @extends Roo.bootstrap.Component
37070  * Base class for layout managers.
37071  */
37072 Roo.bootstrap.layout.Manager = function(config)
37073 {
37074     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37075
37076
37077
37078
37079
37080     /** false to disable window resize monitoring @type Boolean */
37081     this.monitorWindowResize = true;
37082     this.regions = {};
37083     this.addEvents({
37084         /**
37085          * @event layout
37086          * Fires when a layout is performed.
37087          * @param {Roo.LayoutManager} this
37088          */
37089         "layout" : true,
37090         /**
37091          * @event regionresized
37092          * Fires when the user resizes a region.
37093          * @param {Roo.LayoutRegion} region The resized region
37094          * @param {Number} newSize The new size (width for east/west, height for north/south)
37095          */
37096         "regionresized" : true,
37097         /**
37098          * @event regioncollapsed
37099          * Fires when a region is collapsed.
37100          * @param {Roo.LayoutRegion} region The collapsed region
37101          */
37102         "regioncollapsed" : true,
37103         /**
37104          * @event regionexpanded
37105          * Fires when a region is expanded.
37106          * @param {Roo.LayoutRegion} region The expanded region
37107          */
37108         "regionexpanded" : true
37109     });
37110     this.updating = false;
37111
37112     if (config.el) {
37113         this.el = Roo.get(config.el);
37114         this.initEvents();
37115     }
37116
37117 };
37118
37119 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37120
37121
37122     regions : null,
37123
37124     monitorWindowResize : true,
37125
37126
37127     updating : false,
37128
37129
37130     onRender : function(ct, position)
37131     {
37132         if(!this.el){
37133             this.el = Roo.get(ct);
37134             this.initEvents();
37135         }
37136         //this.fireEvent('render',this);
37137     },
37138
37139
37140     initEvents: function()
37141     {
37142
37143
37144         // ie scrollbar fix
37145         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37146             document.body.scroll = "no";
37147         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37148             this.el.position('relative');
37149         }
37150         this.id = this.el.id;
37151         this.el.addClass("roo-layout-container");
37152         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37153         if(this.el.dom != document.body ) {
37154             this.el.on('resize', this.layout,this);
37155             this.el.on('show', this.layout,this);
37156         }
37157
37158     },
37159
37160     /**
37161      * Returns true if this layout is currently being updated
37162      * @return {Boolean}
37163      */
37164     isUpdating : function(){
37165         return this.updating;
37166     },
37167
37168     /**
37169      * Suspend the LayoutManager from doing auto-layouts while
37170      * making multiple add or remove calls
37171      */
37172     beginUpdate : function(){
37173         this.updating = true;
37174     },
37175
37176     /**
37177      * Restore auto-layouts and optionally disable the manager from performing a layout
37178      * @param {Boolean} noLayout true to disable a layout update
37179      */
37180     endUpdate : function(noLayout){
37181         this.updating = false;
37182         if(!noLayout){
37183             this.layout();
37184         }
37185     },
37186
37187     layout: function(){
37188         // abstract...
37189     },
37190
37191     onRegionResized : function(region, newSize){
37192         this.fireEvent("regionresized", region, newSize);
37193         this.layout();
37194     },
37195
37196     onRegionCollapsed : function(region){
37197         this.fireEvent("regioncollapsed", region);
37198     },
37199
37200     onRegionExpanded : function(region){
37201         this.fireEvent("regionexpanded", region);
37202     },
37203
37204     /**
37205      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37206      * performs box-model adjustments.
37207      * @return {Object} The size as an object {width: (the width), height: (the height)}
37208      */
37209     getViewSize : function()
37210     {
37211         var size;
37212         if(this.el.dom != document.body){
37213             size = this.el.getSize();
37214         }else{
37215             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37216         }
37217         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37218         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37219         return size;
37220     },
37221
37222     /**
37223      * Returns the Element this layout is bound to.
37224      * @return {Roo.Element}
37225      */
37226     getEl : function(){
37227         return this.el;
37228     },
37229
37230     /**
37231      * Returns the specified region.
37232      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37233      * @return {Roo.LayoutRegion}
37234      */
37235     getRegion : function(target){
37236         return this.regions[target.toLowerCase()];
37237     },
37238
37239     onWindowResize : function(){
37240         if(this.monitorWindowResize){
37241             this.layout();
37242         }
37243     }
37244 });
37245 /*
37246  * Based on:
37247  * Ext JS Library 1.1.1
37248  * Copyright(c) 2006-2007, Ext JS, LLC.
37249  *
37250  * Originally Released Under LGPL - original licence link has changed is not relivant.
37251  *
37252  * Fork - LGPL
37253  * <script type="text/javascript">
37254  */
37255 /**
37256  * @class Roo.bootstrap.layout.Border
37257  * @extends Roo.bootstrap.layout.Manager
37258  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37259  * please see: examples/bootstrap/nested.html<br><br>
37260  
37261 <b>The container the layout is rendered into can be either the body element or any other element.
37262 If it is not the body element, the container needs to either be an absolute positioned element,
37263 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37264 the container size if it is not the body element.</b>
37265
37266 * @constructor
37267 * Create a new Border
37268 * @param {Object} config Configuration options
37269  */
37270 Roo.bootstrap.layout.Border = function(config){
37271     config = config || {};
37272     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37273     
37274     
37275     
37276     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37277         if(config[region]){
37278             config[region].region = region;
37279             this.addRegion(config[region]);
37280         }
37281     },this);
37282     
37283 };
37284
37285 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37286
37287 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37288     
37289     parent : false, // this might point to a 'nest' or a ???
37290     
37291     /**
37292      * Creates and adds a new region if it doesn't already exist.
37293      * @param {String} target The target region key (north, south, east, west or center).
37294      * @param {Object} config The regions config object
37295      * @return {BorderLayoutRegion} The new region
37296      */
37297     addRegion : function(config)
37298     {
37299         if(!this.regions[config.region]){
37300             var r = this.factory(config);
37301             this.bindRegion(r);
37302         }
37303         return this.regions[config.region];
37304     },
37305
37306     // private (kinda)
37307     bindRegion : function(r){
37308         this.regions[r.config.region] = r;
37309         
37310         r.on("visibilitychange",    this.layout, this);
37311         r.on("paneladded",          this.layout, this);
37312         r.on("panelremoved",        this.layout, this);
37313         r.on("invalidated",         this.layout, this);
37314         r.on("resized",             this.onRegionResized, this);
37315         r.on("collapsed",           this.onRegionCollapsed, this);
37316         r.on("expanded",            this.onRegionExpanded, this);
37317     },
37318
37319     /**
37320      * Performs a layout update.
37321      */
37322     layout : function()
37323     {
37324         if(this.updating) {
37325             return;
37326         }
37327         
37328         // render all the rebions if they have not been done alreayd?
37329         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37330             if(this.regions[region] && !this.regions[region].bodyEl){
37331                 this.regions[region].onRender(this.el)
37332             }
37333         },this);
37334         
37335         var size = this.getViewSize();
37336         var w = size.width;
37337         var h = size.height;
37338         var centerW = w;
37339         var centerH = h;
37340         var centerY = 0;
37341         var centerX = 0;
37342         //var x = 0, y = 0;
37343
37344         var rs = this.regions;
37345         var north = rs["north"];
37346         var south = rs["south"]; 
37347         var west = rs["west"];
37348         var east = rs["east"];
37349         var center = rs["center"];
37350         //if(this.hideOnLayout){ // not supported anymore
37351             //c.el.setStyle("display", "none");
37352         //}
37353         if(north && north.isVisible()){
37354             var b = north.getBox();
37355             var m = north.getMargins();
37356             b.width = w - (m.left+m.right);
37357             b.x = m.left;
37358             b.y = m.top;
37359             centerY = b.height + b.y + m.bottom;
37360             centerH -= centerY;
37361             north.updateBox(this.safeBox(b));
37362         }
37363         if(south && south.isVisible()){
37364             var b = south.getBox();
37365             var m = south.getMargins();
37366             b.width = w - (m.left+m.right);
37367             b.x = m.left;
37368             var totalHeight = (b.height + m.top + m.bottom);
37369             b.y = h - totalHeight + m.top;
37370             centerH -= totalHeight;
37371             south.updateBox(this.safeBox(b));
37372         }
37373         if(west && west.isVisible()){
37374             var b = west.getBox();
37375             var m = west.getMargins();
37376             b.height = centerH - (m.top+m.bottom);
37377             b.x = m.left;
37378             b.y = centerY + m.top;
37379             var totalWidth = (b.width + m.left + m.right);
37380             centerX += totalWidth;
37381             centerW -= totalWidth;
37382             west.updateBox(this.safeBox(b));
37383         }
37384         if(east && east.isVisible()){
37385             var b = east.getBox();
37386             var m = east.getMargins();
37387             b.height = centerH - (m.top+m.bottom);
37388             var totalWidth = (b.width + m.left + m.right);
37389             b.x = w - totalWidth + m.left;
37390             b.y = centerY + m.top;
37391             centerW -= totalWidth;
37392             east.updateBox(this.safeBox(b));
37393         }
37394         if(center){
37395             var m = center.getMargins();
37396             var centerBox = {
37397                 x: centerX + m.left,
37398                 y: centerY + m.top,
37399                 width: centerW - (m.left+m.right),
37400                 height: centerH - (m.top+m.bottom)
37401             };
37402             //if(this.hideOnLayout){
37403                 //center.el.setStyle("display", "block");
37404             //}
37405             center.updateBox(this.safeBox(centerBox));
37406         }
37407         this.el.repaint();
37408         this.fireEvent("layout", this);
37409     },
37410
37411     // private
37412     safeBox : function(box){
37413         box.width = Math.max(0, box.width);
37414         box.height = Math.max(0, box.height);
37415         return box;
37416     },
37417
37418     /**
37419      * Adds a ContentPanel (or subclass) to this layout.
37420      * @param {String} target The target region key (north, south, east, west or center).
37421      * @param {Roo.ContentPanel} panel The panel to add
37422      * @return {Roo.ContentPanel} The added panel
37423      */
37424     add : function(target, panel){
37425          
37426         target = target.toLowerCase();
37427         return this.regions[target].add(panel);
37428     },
37429
37430     /**
37431      * Remove a ContentPanel (or subclass) to this layout.
37432      * @param {String} target The target region key (north, south, east, west or center).
37433      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37434      * @return {Roo.ContentPanel} The removed panel
37435      */
37436     remove : function(target, panel){
37437         target = target.toLowerCase();
37438         return this.regions[target].remove(panel);
37439     },
37440
37441     /**
37442      * Searches all regions for a panel with the specified id
37443      * @param {String} panelId
37444      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37445      */
37446     findPanel : function(panelId){
37447         var rs = this.regions;
37448         for(var target in rs){
37449             if(typeof rs[target] != "function"){
37450                 var p = rs[target].getPanel(panelId);
37451                 if(p){
37452                     return p;
37453                 }
37454             }
37455         }
37456         return null;
37457     },
37458
37459     /**
37460      * Searches all regions for a panel with the specified id and activates (shows) it.
37461      * @param {String/ContentPanel} panelId The panels id or the panel itself
37462      * @return {Roo.ContentPanel} The shown panel or null
37463      */
37464     showPanel : function(panelId) {
37465       var rs = this.regions;
37466       for(var target in rs){
37467          var r = rs[target];
37468          if(typeof r != "function"){
37469             if(r.hasPanel(panelId)){
37470                return r.showPanel(panelId);
37471             }
37472          }
37473       }
37474       return null;
37475    },
37476
37477    /**
37478      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37479      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37480      */
37481    /*
37482     restoreState : function(provider){
37483         if(!provider){
37484             provider = Roo.state.Manager;
37485         }
37486         var sm = new Roo.LayoutStateManager();
37487         sm.init(this, provider);
37488     },
37489 */
37490  
37491  
37492     /**
37493      * Adds a xtype elements to the layout.
37494      * <pre><code>
37495
37496 layout.addxtype({
37497        xtype : 'ContentPanel',
37498        region: 'west',
37499        items: [ .... ]
37500    }
37501 );
37502
37503 layout.addxtype({
37504         xtype : 'NestedLayoutPanel',
37505         region: 'west',
37506         layout: {
37507            center: { },
37508            west: { }   
37509         },
37510         items : [ ... list of content panels or nested layout panels.. ]
37511    }
37512 );
37513 </code></pre>
37514      * @param {Object} cfg Xtype definition of item to add.
37515      */
37516     addxtype : function(cfg)
37517     {
37518         // basically accepts a pannel...
37519         // can accept a layout region..!?!?
37520         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37521         
37522         
37523         // theory?  children can only be panels??
37524         
37525         //if (!cfg.xtype.match(/Panel$/)) {
37526         //    return false;
37527         //}
37528         var ret = false;
37529         
37530         if (typeof(cfg.region) == 'undefined') {
37531             Roo.log("Failed to add Panel, region was not set");
37532             Roo.log(cfg);
37533             return false;
37534         }
37535         var region = cfg.region;
37536         delete cfg.region;
37537         
37538           
37539         var xitems = [];
37540         if (cfg.items) {
37541             xitems = cfg.items;
37542             delete cfg.items;
37543         }
37544         var nb = false;
37545         
37546         if ( region == 'center') {
37547             Roo.log("Center: " + cfg.title);
37548         }
37549         
37550         
37551         switch(cfg.xtype) 
37552         {
37553             case 'Content':  // ContentPanel (el, cfg)
37554             case 'Scroll':  // ContentPanel (el, cfg)
37555             case 'View': 
37556                 cfg.autoCreate = cfg.autoCreate || true;
37557                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37558                 //} else {
37559                 //    var el = this.el.createChild();
37560                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37561                 //}
37562                 
37563                 this.add(region, ret);
37564                 break;
37565             
37566             /*
37567             case 'TreePanel': // our new panel!
37568                 cfg.el = this.el.createChild();
37569                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37570                 this.add(region, ret);
37571                 break;
37572             */
37573             
37574             case 'Nest': 
37575                 // create a new Layout (which is  a Border Layout...
37576                 
37577                 var clayout = cfg.layout;
37578                 clayout.el  = this.el.createChild();
37579                 clayout.items   = clayout.items  || [];
37580                 
37581                 delete cfg.layout;
37582                 
37583                 // replace this exitems with the clayout ones..
37584                 xitems = clayout.items;
37585                  
37586                 // force background off if it's in center...
37587                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37588                     cfg.background = false;
37589                 }
37590                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37591                 
37592                 
37593                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37594                 //console.log('adding nested layout panel '  + cfg.toSource());
37595                 this.add(region, ret);
37596                 nb = {}; /// find first...
37597                 break;
37598             
37599             case 'Grid':
37600                 
37601                 // needs grid and region
37602                 
37603                 //var el = this.getRegion(region).el.createChild();
37604                 /*
37605                  *var el = this.el.createChild();
37606                 // create the grid first...
37607                 cfg.grid.container = el;
37608                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37609                 */
37610                 
37611                 if (region == 'center' && this.active ) {
37612                     cfg.background = false;
37613                 }
37614                 
37615                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37616                 
37617                 this.add(region, ret);
37618                 /*
37619                 if (cfg.background) {
37620                     // render grid on panel activation (if panel background)
37621                     ret.on('activate', function(gp) {
37622                         if (!gp.grid.rendered) {
37623                     //        gp.grid.render(el);
37624                         }
37625                     });
37626                 } else {
37627                   //  cfg.grid.render(el);
37628                 }
37629                 */
37630                 break;
37631            
37632            
37633             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37634                 // it was the old xcomponent building that caused this before.
37635                 // espeically if border is the top element in the tree.
37636                 ret = this;
37637                 break; 
37638                 
37639                     
37640                 
37641                 
37642                 
37643             default:
37644                 /*
37645                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37646                     
37647                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37648                     this.add(region, ret);
37649                 } else {
37650                 */
37651                     Roo.log(cfg);
37652                     throw "Can not add '" + cfg.xtype + "' to Border";
37653                     return null;
37654              
37655                                 
37656              
37657         }
37658         this.beginUpdate();
37659         // add children..
37660         var region = '';
37661         var abn = {};
37662         Roo.each(xitems, function(i)  {
37663             region = nb && i.region ? i.region : false;
37664             
37665             var add = ret.addxtype(i);
37666            
37667             if (region) {
37668                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37669                 if (!i.background) {
37670                     abn[region] = nb[region] ;
37671                 }
37672             }
37673             
37674         });
37675         this.endUpdate();
37676
37677         // make the last non-background panel active..
37678         //if (nb) { Roo.log(abn); }
37679         if (nb) {
37680             
37681             for(var r in abn) {
37682                 region = this.getRegion(r);
37683                 if (region) {
37684                     // tried using nb[r], but it does not work..
37685                      
37686                     region.showPanel(abn[r]);
37687                    
37688                 }
37689             }
37690         }
37691         return ret;
37692         
37693     },
37694     
37695     
37696 // private
37697     factory : function(cfg)
37698     {
37699         
37700         var validRegions = Roo.bootstrap.layout.Border.regions;
37701
37702         var target = cfg.region;
37703         cfg.mgr = this;
37704         
37705         var r = Roo.bootstrap.layout;
37706         Roo.log(target);
37707         switch(target){
37708             case "north":
37709                 return new r.North(cfg);
37710             case "south":
37711                 return new r.South(cfg);
37712             case "east":
37713                 return new r.East(cfg);
37714             case "west":
37715                 return new r.West(cfg);
37716             case "center":
37717                 return new r.Center(cfg);
37718         }
37719         throw 'Layout region "'+target+'" not supported.';
37720     }
37721     
37722     
37723 });
37724  /*
37725  * Based on:
37726  * Ext JS Library 1.1.1
37727  * Copyright(c) 2006-2007, Ext JS, LLC.
37728  *
37729  * Originally Released Under LGPL - original licence link has changed is not relivant.
37730  *
37731  * Fork - LGPL
37732  * <script type="text/javascript">
37733  */
37734  
37735 /**
37736  * @class Roo.bootstrap.layout.Basic
37737  * @extends Roo.util.Observable
37738  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37739  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37740  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37741  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37742  * @cfg {string}   region  the region that it inhabits..
37743  * @cfg {bool}   skipConfig skip config?
37744  * 
37745
37746  */
37747 Roo.bootstrap.layout.Basic = function(config){
37748     
37749     this.mgr = config.mgr;
37750     
37751     this.position = config.region;
37752     
37753     var skipConfig = config.skipConfig;
37754     
37755     this.events = {
37756         /**
37757          * @scope Roo.BasicLayoutRegion
37758          */
37759         
37760         /**
37761          * @event beforeremove
37762          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37763          * @param {Roo.LayoutRegion} this
37764          * @param {Roo.ContentPanel} panel The panel
37765          * @param {Object} e The cancel event object
37766          */
37767         "beforeremove" : true,
37768         /**
37769          * @event invalidated
37770          * Fires when the layout for this region is changed.
37771          * @param {Roo.LayoutRegion} this
37772          */
37773         "invalidated" : true,
37774         /**
37775          * @event visibilitychange
37776          * Fires when this region is shown or hidden 
37777          * @param {Roo.LayoutRegion} this
37778          * @param {Boolean} visibility true or false
37779          */
37780         "visibilitychange" : true,
37781         /**
37782          * @event paneladded
37783          * Fires when a panel is added. 
37784          * @param {Roo.LayoutRegion} this
37785          * @param {Roo.ContentPanel} panel The panel
37786          */
37787         "paneladded" : true,
37788         /**
37789          * @event panelremoved
37790          * Fires when a panel is removed. 
37791          * @param {Roo.LayoutRegion} this
37792          * @param {Roo.ContentPanel} panel The panel
37793          */
37794         "panelremoved" : true,
37795         /**
37796          * @event beforecollapse
37797          * Fires when this region before collapse.
37798          * @param {Roo.LayoutRegion} this
37799          */
37800         "beforecollapse" : true,
37801         /**
37802          * @event collapsed
37803          * Fires when this region is collapsed.
37804          * @param {Roo.LayoutRegion} this
37805          */
37806         "collapsed" : true,
37807         /**
37808          * @event expanded
37809          * Fires when this region is expanded.
37810          * @param {Roo.LayoutRegion} this
37811          */
37812         "expanded" : true,
37813         /**
37814          * @event slideshow
37815          * Fires when this region is slid into view.
37816          * @param {Roo.LayoutRegion} this
37817          */
37818         "slideshow" : true,
37819         /**
37820          * @event slidehide
37821          * Fires when this region slides out of view. 
37822          * @param {Roo.LayoutRegion} this
37823          */
37824         "slidehide" : true,
37825         /**
37826          * @event panelactivated
37827          * Fires when a panel is activated. 
37828          * @param {Roo.LayoutRegion} this
37829          * @param {Roo.ContentPanel} panel The activated panel
37830          */
37831         "panelactivated" : true,
37832         /**
37833          * @event resized
37834          * Fires when the user resizes this region. 
37835          * @param {Roo.LayoutRegion} this
37836          * @param {Number} newSize The new size (width for east/west, height for north/south)
37837          */
37838         "resized" : true
37839     };
37840     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37841     this.panels = new Roo.util.MixedCollection();
37842     this.panels.getKey = this.getPanelId.createDelegate(this);
37843     this.box = null;
37844     this.activePanel = null;
37845     // ensure listeners are added...
37846     
37847     if (config.listeners || config.events) {
37848         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37849             listeners : config.listeners || {},
37850             events : config.events || {}
37851         });
37852     }
37853     
37854     if(skipConfig !== true){
37855         this.applyConfig(config);
37856     }
37857 };
37858
37859 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37860 {
37861     getPanelId : function(p){
37862         return p.getId();
37863     },
37864     
37865     applyConfig : function(config){
37866         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37867         this.config = config;
37868         
37869     },
37870     
37871     /**
37872      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37873      * the width, for horizontal (north, south) the height.
37874      * @param {Number} newSize The new width or height
37875      */
37876     resizeTo : function(newSize){
37877         var el = this.el ? this.el :
37878                  (this.activePanel ? this.activePanel.getEl() : null);
37879         if(el){
37880             switch(this.position){
37881                 case "east":
37882                 case "west":
37883                     el.setWidth(newSize);
37884                     this.fireEvent("resized", this, newSize);
37885                 break;
37886                 case "north":
37887                 case "south":
37888                     el.setHeight(newSize);
37889                     this.fireEvent("resized", this, newSize);
37890                 break;                
37891             }
37892         }
37893     },
37894     
37895     getBox : function(){
37896         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37897     },
37898     
37899     getMargins : function(){
37900         return this.margins;
37901     },
37902     
37903     updateBox : function(box){
37904         this.box = box;
37905         var el = this.activePanel.getEl();
37906         el.dom.style.left = box.x + "px";
37907         el.dom.style.top = box.y + "px";
37908         this.activePanel.setSize(box.width, box.height);
37909     },
37910     
37911     /**
37912      * Returns the container element for this region.
37913      * @return {Roo.Element}
37914      */
37915     getEl : function(){
37916         return this.activePanel;
37917     },
37918     
37919     /**
37920      * Returns true if this region is currently visible.
37921      * @return {Boolean}
37922      */
37923     isVisible : function(){
37924         return this.activePanel ? true : false;
37925     },
37926     
37927     setActivePanel : function(panel){
37928         panel = this.getPanel(panel);
37929         if(this.activePanel && this.activePanel != panel){
37930             this.activePanel.setActiveState(false);
37931             this.activePanel.getEl().setLeftTop(-10000,-10000);
37932         }
37933         this.activePanel = panel;
37934         panel.setActiveState(true);
37935         if(this.box){
37936             panel.setSize(this.box.width, this.box.height);
37937         }
37938         this.fireEvent("panelactivated", this, panel);
37939         this.fireEvent("invalidated");
37940     },
37941     
37942     /**
37943      * Show the specified panel.
37944      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37945      * @return {Roo.ContentPanel} The shown panel or null
37946      */
37947     showPanel : function(panel){
37948         panel = this.getPanel(panel);
37949         if(panel){
37950             this.setActivePanel(panel);
37951         }
37952         return panel;
37953     },
37954     
37955     /**
37956      * Get the active panel for this region.
37957      * @return {Roo.ContentPanel} The active panel or null
37958      */
37959     getActivePanel : function(){
37960         return this.activePanel;
37961     },
37962     
37963     /**
37964      * Add the passed ContentPanel(s)
37965      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37966      * @return {Roo.ContentPanel} The panel added (if only one was added)
37967      */
37968     add : function(panel){
37969         if(arguments.length > 1){
37970             for(var i = 0, len = arguments.length; i < len; i++) {
37971                 this.add(arguments[i]);
37972             }
37973             return null;
37974         }
37975         if(this.hasPanel(panel)){
37976             this.showPanel(panel);
37977             return panel;
37978         }
37979         var el = panel.getEl();
37980         if(el.dom.parentNode != this.mgr.el.dom){
37981             this.mgr.el.dom.appendChild(el.dom);
37982         }
37983         if(panel.setRegion){
37984             panel.setRegion(this);
37985         }
37986         this.panels.add(panel);
37987         el.setStyle("position", "absolute");
37988         if(!panel.background){
37989             this.setActivePanel(panel);
37990             if(this.config.initialSize && this.panels.getCount()==1){
37991                 this.resizeTo(this.config.initialSize);
37992             }
37993         }
37994         this.fireEvent("paneladded", this, panel);
37995         return panel;
37996     },
37997     
37998     /**
37999      * Returns true if the panel is in this region.
38000      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38001      * @return {Boolean}
38002      */
38003     hasPanel : function(panel){
38004         if(typeof panel == "object"){ // must be panel obj
38005             panel = panel.getId();
38006         }
38007         return this.getPanel(panel) ? true : false;
38008     },
38009     
38010     /**
38011      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38012      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38013      * @param {Boolean} preservePanel Overrides the config preservePanel option
38014      * @return {Roo.ContentPanel} The panel that was removed
38015      */
38016     remove : function(panel, preservePanel){
38017         panel = this.getPanel(panel);
38018         if(!panel){
38019             return null;
38020         }
38021         var e = {};
38022         this.fireEvent("beforeremove", this, panel, e);
38023         if(e.cancel === true){
38024             return null;
38025         }
38026         var panelId = panel.getId();
38027         this.panels.removeKey(panelId);
38028         return panel;
38029     },
38030     
38031     /**
38032      * Returns the panel specified or null if it's not in this region.
38033      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38034      * @return {Roo.ContentPanel}
38035      */
38036     getPanel : function(id){
38037         if(typeof id == "object"){ // must be panel obj
38038             return id;
38039         }
38040         return this.panels.get(id);
38041     },
38042     
38043     /**
38044      * Returns this regions position (north/south/east/west/center).
38045      * @return {String} 
38046      */
38047     getPosition: function(){
38048         return this.position;    
38049     }
38050 });/*
38051  * Based on:
38052  * Ext JS Library 1.1.1
38053  * Copyright(c) 2006-2007, Ext JS, LLC.
38054  *
38055  * Originally Released Under LGPL - original licence link has changed is not relivant.
38056  *
38057  * Fork - LGPL
38058  * <script type="text/javascript">
38059  */
38060  
38061 /**
38062  * @class Roo.bootstrap.layout.Region
38063  * @extends Roo.bootstrap.layout.Basic
38064  * This class represents a region in a layout manager.
38065  
38066  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38067  * @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})
38068  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38069  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38070  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38071  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38072  * @cfg {String}    title           The title for the region (overrides panel titles)
38073  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38074  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38075  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38076  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38077  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38078  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38079  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38080  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38081  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38082  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38083
38084  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38085  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38086  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38087  * @cfg {Number}    width           For East/West panels
38088  * @cfg {Number}    height          For North/South panels
38089  * @cfg {Boolean}   split           To show the splitter
38090  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38091  * 
38092  * @cfg {string}   cls             Extra CSS classes to add to region
38093  * 
38094  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38095  * @cfg {string}   region  the region that it inhabits..
38096  *
38097
38098  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38099  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38100
38101  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38102  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38103  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38104  */
38105 Roo.bootstrap.layout.Region = function(config)
38106 {
38107     this.applyConfig(config);
38108
38109     var mgr = config.mgr;
38110     var pos = config.region;
38111     config.skipConfig = true;
38112     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38113     
38114     if (mgr.el) {
38115         this.onRender(mgr.el);   
38116     }
38117      
38118     this.visible = true;
38119     this.collapsed = false;
38120     this.unrendered_panels = [];
38121 };
38122
38123 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38124
38125     position: '', // set by wrapper (eg. north/south etc..)
38126     unrendered_panels : null,  // unrendered panels.
38127     
38128     tabPosition : false,
38129     
38130     mgr: false, // points to 'Border'
38131     
38132     
38133     createBody : function(){
38134         /** This region's body element 
38135         * @type Roo.Element */
38136         this.bodyEl = this.el.createChild({
38137                 tag: "div",
38138                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38139         });
38140     },
38141
38142     onRender: function(ctr, pos)
38143     {
38144         var dh = Roo.DomHelper;
38145         /** This region's container element 
38146         * @type Roo.Element */
38147         this.el = dh.append(ctr.dom, {
38148                 tag: "div",
38149                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38150             }, true);
38151         /** This region's title element 
38152         * @type Roo.Element */
38153     
38154         this.titleEl = dh.append(this.el.dom,  {
38155                 tag: "div",
38156                 unselectable: "on",
38157                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38158                 children:[
38159                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38160                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38161                 ]
38162             }, true);
38163         
38164         this.titleEl.enableDisplayMode();
38165         /** This region's title text element 
38166         * @type HTMLElement */
38167         this.titleTextEl = this.titleEl.dom.firstChild;
38168         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38169         /*
38170         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38171         this.closeBtn.enableDisplayMode();
38172         this.closeBtn.on("click", this.closeClicked, this);
38173         this.closeBtn.hide();
38174     */
38175         this.createBody(this.config);
38176         if(this.config.hideWhenEmpty){
38177             this.hide();
38178             this.on("paneladded", this.validateVisibility, this);
38179             this.on("panelremoved", this.validateVisibility, this);
38180         }
38181         if(this.autoScroll){
38182             this.bodyEl.setStyle("overflow", "auto");
38183         }else{
38184             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38185         }
38186         //if(c.titlebar !== false){
38187             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38188                 this.titleEl.hide();
38189             }else{
38190                 this.titleEl.show();
38191                 if(this.config.title){
38192                     this.titleTextEl.innerHTML = this.config.title;
38193                 }
38194             }
38195         //}
38196         if(this.config.collapsed){
38197             this.collapse(true);
38198         }
38199         if(this.config.hidden){
38200             this.hide();
38201         }
38202         
38203         if (this.unrendered_panels && this.unrendered_panels.length) {
38204             for (var i =0;i< this.unrendered_panels.length; i++) {
38205                 this.add(this.unrendered_panels[i]);
38206             }
38207             this.unrendered_panels = null;
38208             
38209         }
38210         
38211     },
38212     
38213     applyConfig : function(c)
38214     {
38215         /*
38216          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38217             var dh = Roo.DomHelper;
38218             if(c.titlebar !== false){
38219                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38220                 this.collapseBtn.on("click", this.collapse, this);
38221                 this.collapseBtn.enableDisplayMode();
38222                 /*
38223                 if(c.showPin === true || this.showPin){
38224                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38225                     this.stickBtn.enableDisplayMode();
38226                     this.stickBtn.on("click", this.expand, this);
38227                     this.stickBtn.hide();
38228                 }
38229                 
38230             }
38231             */
38232             /** This region's collapsed element
38233             * @type Roo.Element */
38234             /*
38235              *
38236             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38237                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38238             ]}, true);
38239             
38240             if(c.floatable !== false){
38241                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38242                this.collapsedEl.on("click", this.collapseClick, this);
38243             }
38244
38245             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38246                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38247                    id: "message", unselectable: "on", style:{"float":"left"}});
38248                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38249              }
38250             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38251             this.expandBtn.on("click", this.expand, this);
38252             
38253         }
38254         
38255         if(this.collapseBtn){
38256             this.collapseBtn.setVisible(c.collapsible == true);
38257         }
38258         
38259         this.cmargins = c.cmargins || this.cmargins ||
38260                          (this.position == "west" || this.position == "east" ?
38261                              {top: 0, left: 2, right:2, bottom: 0} :
38262                              {top: 2, left: 0, right:0, bottom: 2});
38263         */
38264         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38265         
38266         
38267         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38268         
38269         this.autoScroll = c.autoScroll || false;
38270         
38271         
38272        
38273         
38274         this.duration = c.duration || .30;
38275         this.slideDuration = c.slideDuration || .45;
38276         this.config = c;
38277        
38278     },
38279     /**
38280      * Returns true if this region is currently visible.
38281      * @return {Boolean}
38282      */
38283     isVisible : function(){
38284         return this.visible;
38285     },
38286
38287     /**
38288      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38289      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38290      */
38291     //setCollapsedTitle : function(title){
38292     //    title = title || "&#160;";
38293      //   if(this.collapsedTitleTextEl){
38294       //      this.collapsedTitleTextEl.innerHTML = title;
38295        // }
38296     //},
38297
38298     getBox : function(){
38299         var b;
38300       //  if(!this.collapsed){
38301             b = this.el.getBox(false, true);
38302        // }else{
38303           //  b = this.collapsedEl.getBox(false, true);
38304         //}
38305         return b;
38306     },
38307
38308     getMargins : function(){
38309         return this.margins;
38310         //return this.collapsed ? this.cmargins : this.margins;
38311     },
38312 /*
38313     highlight : function(){
38314         this.el.addClass("x-layout-panel-dragover");
38315     },
38316
38317     unhighlight : function(){
38318         this.el.removeClass("x-layout-panel-dragover");
38319     },
38320 */
38321     updateBox : function(box)
38322     {
38323         if (!this.bodyEl) {
38324             return; // not rendered yet..
38325         }
38326         
38327         this.box = box;
38328         if(!this.collapsed){
38329             this.el.dom.style.left = box.x + "px";
38330             this.el.dom.style.top = box.y + "px";
38331             this.updateBody(box.width, box.height);
38332         }else{
38333             this.collapsedEl.dom.style.left = box.x + "px";
38334             this.collapsedEl.dom.style.top = box.y + "px";
38335             this.collapsedEl.setSize(box.width, box.height);
38336         }
38337         if(this.tabs){
38338             this.tabs.autoSizeTabs();
38339         }
38340     },
38341
38342     updateBody : function(w, h)
38343     {
38344         if(w !== null){
38345             this.el.setWidth(w);
38346             w -= this.el.getBorderWidth("rl");
38347             if(this.config.adjustments){
38348                 w += this.config.adjustments[0];
38349             }
38350         }
38351         if(h !== null && h > 0){
38352             this.el.setHeight(h);
38353             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38354             h -= this.el.getBorderWidth("tb");
38355             if(this.config.adjustments){
38356                 h += this.config.adjustments[1];
38357             }
38358             this.bodyEl.setHeight(h);
38359             if(this.tabs){
38360                 h = this.tabs.syncHeight(h);
38361             }
38362         }
38363         if(this.panelSize){
38364             w = w !== null ? w : this.panelSize.width;
38365             h = h !== null ? h : this.panelSize.height;
38366         }
38367         if(this.activePanel){
38368             var el = this.activePanel.getEl();
38369             w = w !== null ? w : el.getWidth();
38370             h = h !== null ? h : el.getHeight();
38371             this.panelSize = {width: w, height: h};
38372             this.activePanel.setSize(w, h);
38373         }
38374         if(Roo.isIE && this.tabs){
38375             this.tabs.el.repaint();
38376         }
38377     },
38378
38379     /**
38380      * Returns the container element for this region.
38381      * @return {Roo.Element}
38382      */
38383     getEl : function(){
38384         return this.el;
38385     },
38386
38387     /**
38388      * Hides this region.
38389      */
38390     hide : function(){
38391         //if(!this.collapsed){
38392             this.el.dom.style.left = "-2000px";
38393             this.el.hide();
38394         //}else{
38395          //   this.collapsedEl.dom.style.left = "-2000px";
38396          //   this.collapsedEl.hide();
38397        // }
38398         this.visible = false;
38399         this.fireEvent("visibilitychange", this, false);
38400     },
38401
38402     /**
38403      * Shows this region if it was previously hidden.
38404      */
38405     show : function(){
38406         //if(!this.collapsed){
38407             this.el.show();
38408         //}else{
38409         //    this.collapsedEl.show();
38410        // }
38411         this.visible = true;
38412         this.fireEvent("visibilitychange", this, true);
38413     },
38414 /*
38415     closeClicked : function(){
38416         if(this.activePanel){
38417             this.remove(this.activePanel);
38418         }
38419     },
38420
38421     collapseClick : function(e){
38422         if(this.isSlid){
38423            e.stopPropagation();
38424            this.slideIn();
38425         }else{
38426            e.stopPropagation();
38427            this.slideOut();
38428         }
38429     },
38430 */
38431     /**
38432      * Collapses this region.
38433      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38434      */
38435     /*
38436     collapse : function(skipAnim, skipCheck = false){
38437         if(this.collapsed) {
38438             return;
38439         }
38440         
38441         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38442             
38443             this.collapsed = true;
38444             if(this.split){
38445                 this.split.el.hide();
38446             }
38447             if(this.config.animate && skipAnim !== true){
38448                 this.fireEvent("invalidated", this);
38449                 this.animateCollapse();
38450             }else{
38451                 this.el.setLocation(-20000,-20000);
38452                 this.el.hide();
38453                 this.collapsedEl.show();
38454                 this.fireEvent("collapsed", this);
38455                 this.fireEvent("invalidated", this);
38456             }
38457         }
38458         
38459     },
38460 */
38461     animateCollapse : function(){
38462         // overridden
38463     },
38464
38465     /**
38466      * Expands this region if it was previously collapsed.
38467      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38468      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38469      */
38470     /*
38471     expand : function(e, skipAnim){
38472         if(e) {
38473             e.stopPropagation();
38474         }
38475         if(!this.collapsed || this.el.hasActiveFx()) {
38476             return;
38477         }
38478         if(this.isSlid){
38479             this.afterSlideIn();
38480             skipAnim = true;
38481         }
38482         this.collapsed = false;
38483         if(this.config.animate && skipAnim !== true){
38484             this.animateExpand();
38485         }else{
38486             this.el.show();
38487             if(this.split){
38488                 this.split.el.show();
38489             }
38490             this.collapsedEl.setLocation(-2000,-2000);
38491             this.collapsedEl.hide();
38492             this.fireEvent("invalidated", this);
38493             this.fireEvent("expanded", this);
38494         }
38495     },
38496 */
38497     animateExpand : function(){
38498         // overridden
38499     },
38500
38501     initTabs : function()
38502     {
38503         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38504         
38505         var ts = new Roo.bootstrap.panel.Tabs({
38506             el: this.bodyEl.dom,
38507             region : this,
38508             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38509             disableTooltips: this.config.disableTabTips,
38510             toolbar : this.config.toolbar
38511         });
38512         
38513         if(this.config.hideTabs){
38514             ts.stripWrap.setDisplayed(false);
38515         }
38516         this.tabs = ts;
38517         ts.resizeTabs = this.config.resizeTabs === true;
38518         ts.minTabWidth = this.config.minTabWidth || 40;
38519         ts.maxTabWidth = this.config.maxTabWidth || 250;
38520         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38521         ts.monitorResize = false;
38522         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38523         ts.bodyEl.addClass('roo-layout-tabs-body');
38524         this.panels.each(this.initPanelAsTab, this);
38525     },
38526
38527     initPanelAsTab : function(panel){
38528         var ti = this.tabs.addTab(
38529             panel.getEl().id,
38530             panel.getTitle(),
38531             null,
38532             this.config.closeOnTab && panel.isClosable(),
38533             panel.tpl
38534         );
38535         if(panel.tabTip !== undefined){
38536             ti.setTooltip(panel.tabTip);
38537         }
38538         ti.on("activate", function(){
38539               this.setActivePanel(panel);
38540         }, this);
38541         
38542         if(this.config.closeOnTab){
38543             ti.on("beforeclose", function(t, e){
38544                 e.cancel = true;
38545                 this.remove(panel);
38546             }, this);
38547         }
38548         
38549         panel.tabItem = ti;
38550         
38551         return ti;
38552     },
38553
38554     updatePanelTitle : function(panel, title)
38555     {
38556         if(this.activePanel == panel){
38557             this.updateTitle(title);
38558         }
38559         if(this.tabs){
38560             var ti = this.tabs.getTab(panel.getEl().id);
38561             ti.setText(title);
38562             if(panel.tabTip !== undefined){
38563                 ti.setTooltip(panel.tabTip);
38564             }
38565         }
38566     },
38567
38568     updateTitle : function(title){
38569         if(this.titleTextEl && !this.config.title){
38570             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38571         }
38572     },
38573
38574     setActivePanel : function(panel)
38575     {
38576         panel = this.getPanel(panel);
38577         if(this.activePanel && this.activePanel != panel){
38578             if(this.activePanel.setActiveState(false) === false){
38579                 return;
38580             }
38581         }
38582         this.activePanel = panel;
38583         panel.setActiveState(true);
38584         if(this.panelSize){
38585             panel.setSize(this.panelSize.width, this.panelSize.height);
38586         }
38587         if(this.closeBtn){
38588             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38589         }
38590         this.updateTitle(panel.getTitle());
38591         if(this.tabs){
38592             this.fireEvent("invalidated", this);
38593         }
38594         this.fireEvent("panelactivated", this, panel);
38595     },
38596
38597     /**
38598      * Shows the specified panel.
38599      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38600      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38601      */
38602     showPanel : function(panel)
38603     {
38604         panel = this.getPanel(panel);
38605         if(panel){
38606             if(this.tabs){
38607                 var tab = this.tabs.getTab(panel.getEl().id);
38608                 if(tab.isHidden()){
38609                     this.tabs.unhideTab(tab.id);
38610                 }
38611                 tab.activate();
38612             }else{
38613                 this.setActivePanel(panel);
38614             }
38615         }
38616         return panel;
38617     },
38618
38619     /**
38620      * Get the active panel for this region.
38621      * @return {Roo.ContentPanel} The active panel or null
38622      */
38623     getActivePanel : function(){
38624         return this.activePanel;
38625     },
38626
38627     validateVisibility : function(){
38628         if(this.panels.getCount() < 1){
38629             this.updateTitle("&#160;");
38630             this.closeBtn.hide();
38631             this.hide();
38632         }else{
38633             if(!this.isVisible()){
38634                 this.show();
38635             }
38636         }
38637     },
38638
38639     /**
38640      * Adds the passed ContentPanel(s) to this region.
38641      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38642      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38643      */
38644     add : function(panel)
38645     {
38646         if(arguments.length > 1){
38647             for(var i = 0, len = arguments.length; i < len; i++) {
38648                 this.add(arguments[i]);
38649             }
38650             return null;
38651         }
38652         
38653         // if we have not been rendered yet, then we can not really do much of this..
38654         if (!this.bodyEl) {
38655             this.unrendered_panels.push(panel);
38656             return panel;
38657         }
38658         
38659         
38660         
38661         
38662         if(this.hasPanel(panel)){
38663             this.showPanel(panel);
38664             return panel;
38665         }
38666         panel.setRegion(this);
38667         this.panels.add(panel);
38668        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38669             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38670             // and hide them... ???
38671             this.bodyEl.dom.appendChild(panel.getEl().dom);
38672             if(panel.background !== true){
38673                 this.setActivePanel(panel);
38674             }
38675             this.fireEvent("paneladded", this, panel);
38676             return panel;
38677         }
38678         */
38679         if(!this.tabs){
38680             this.initTabs();
38681         }else{
38682             this.initPanelAsTab(panel);
38683         }
38684         
38685         
38686         if(panel.background !== true){
38687             this.tabs.activate(panel.getEl().id);
38688         }
38689         this.fireEvent("paneladded", this, panel);
38690         return panel;
38691     },
38692
38693     /**
38694      * Hides the tab for the specified panel.
38695      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38696      */
38697     hidePanel : function(panel){
38698         if(this.tabs && (panel = this.getPanel(panel))){
38699             this.tabs.hideTab(panel.getEl().id);
38700         }
38701     },
38702
38703     /**
38704      * Unhides the tab for a previously hidden panel.
38705      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38706      */
38707     unhidePanel : function(panel){
38708         if(this.tabs && (panel = this.getPanel(panel))){
38709             this.tabs.unhideTab(panel.getEl().id);
38710         }
38711     },
38712
38713     clearPanels : function(){
38714         while(this.panels.getCount() > 0){
38715              this.remove(this.panels.first());
38716         }
38717     },
38718
38719     /**
38720      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38721      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38722      * @param {Boolean} preservePanel Overrides the config preservePanel option
38723      * @return {Roo.ContentPanel} The panel that was removed
38724      */
38725     remove : function(panel, preservePanel)
38726     {
38727         panel = this.getPanel(panel);
38728         if(!panel){
38729             return null;
38730         }
38731         var e = {};
38732         this.fireEvent("beforeremove", this, panel, e);
38733         if(e.cancel === true){
38734             return null;
38735         }
38736         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38737         var panelId = panel.getId();
38738         this.panels.removeKey(panelId);
38739         if(preservePanel){
38740             document.body.appendChild(panel.getEl().dom);
38741         }
38742         if(this.tabs){
38743             this.tabs.removeTab(panel.getEl().id);
38744         }else if (!preservePanel){
38745             this.bodyEl.dom.removeChild(panel.getEl().dom);
38746         }
38747         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38748             var p = this.panels.first();
38749             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38750             tempEl.appendChild(p.getEl().dom);
38751             this.bodyEl.update("");
38752             this.bodyEl.dom.appendChild(p.getEl().dom);
38753             tempEl = null;
38754             this.updateTitle(p.getTitle());
38755             this.tabs = null;
38756             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38757             this.setActivePanel(p);
38758         }
38759         panel.setRegion(null);
38760         if(this.activePanel == panel){
38761             this.activePanel = null;
38762         }
38763         if(this.config.autoDestroy !== false && preservePanel !== true){
38764             try{panel.destroy();}catch(e){}
38765         }
38766         this.fireEvent("panelremoved", this, panel);
38767         return panel;
38768     },
38769
38770     /**
38771      * Returns the TabPanel component used by this region
38772      * @return {Roo.TabPanel}
38773      */
38774     getTabs : function(){
38775         return this.tabs;
38776     },
38777
38778     createTool : function(parentEl, className){
38779         var btn = Roo.DomHelper.append(parentEl, {
38780             tag: "div",
38781             cls: "x-layout-tools-button",
38782             children: [ {
38783                 tag: "div",
38784                 cls: "roo-layout-tools-button-inner " + className,
38785                 html: "&#160;"
38786             }]
38787         }, true);
38788         btn.addClassOnOver("roo-layout-tools-button-over");
38789         return btn;
38790     }
38791 });/*
38792  * Based on:
38793  * Ext JS Library 1.1.1
38794  * Copyright(c) 2006-2007, Ext JS, LLC.
38795  *
38796  * Originally Released Under LGPL - original licence link has changed is not relivant.
38797  *
38798  * Fork - LGPL
38799  * <script type="text/javascript">
38800  */
38801  
38802
38803
38804 /**
38805  * @class Roo.SplitLayoutRegion
38806  * @extends Roo.LayoutRegion
38807  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38808  */
38809 Roo.bootstrap.layout.Split = function(config){
38810     this.cursor = config.cursor;
38811     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38812 };
38813
38814 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38815 {
38816     splitTip : "Drag to resize.",
38817     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38818     useSplitTips : false,
38819
38820     applyConfig : function(config){
38821         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38822     },
38823     
38824     onRender : function(ctr,pos) {
38825         
38826         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38827         if(!this.config.split){
38828             return;
38829         }
38830         if(!this.split){
38831             
38832             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38833                             tag: "div",
38834                             id: this.el.id + "-split",
38835                             cls: "roo-layout-split roo-layout-split-"+this.position,
38836                             html: "&#160;"
38837             });
38838             /** The SplitBar for this region 
38839             * @type Roo.SplitBar */
38840             // does not exist yet...
38841             Roo.log([this.position, this.orientation]);
38842             
38843             this.split = new Roo.bootstrap.SplitBar({
38844                 dragElement : splitEl,
38845                 resizingElement: this.el,
38846                 orientation : this.orientation
38847             });
38848             
38849             this.split.on("moved", this.onSplitMove, this);
38850             this.split.useShim = this.config.useShim === true;
38851             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38852             if(this.useSplitTips){
38853                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38854             }
38855             //if(config.collapsible){
38856             //    this.split.el.on("dblclick", this.collapse,  this);
38857             //}
38858         }
38859         if(typeof this.config.minSize != "undefined"){
38860             this.split.minSize = this.config.minSize;
38861         }
38862         if(typeof this.config.maxSize != "undefined"){
38863             this.split.maxSize = this.config.maxSize;
38864         }
38865         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38866             this.hideSplitter();
38867         }
38868         
38869     },
38870
38871     getHMaxSize : function(){
38872          var cmax = this.config.maxSize || 10000;
38873          var center = this.mgr.getRegion("center");
38874          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38875     },
38876
38877     getVMaxSize : function(){
38878          var cmax = this.config.maxSize || 10000;
38879          var center = this.mgr.getRegion("center");
38880          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38881     },
38882
38883     onSplitMove : function(split, newSize){
38884         this.fireEvent("resized", this, newSize);
38885     },
38886     
38887     /** 
38888      * Returns the {@link Roo.SplitBar} for this region.
38889      * @return {Roo.SplitBar}
38890      */
38891     getSplitBar : function(){
38892         return this.split;
38893     },
38894     
38895     hide : function(){
38896         this.hideSplitter();
38897         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38898     },
38899
38900     hideSplitter : function(){
38901         if(this.split){
38902             this.split.el.setLocation(-2000,-2000);
38903             this.split.el.hide();
38904         }
38905     },
38906
38907     show : function(){
38908         if(this.split){
38909             this.split.el.show();
38910         }
38911         Roo.bootstrap.layout.Split.superclass.show.call(this);
38912     },
38913     
38914     beforeSlide: function(){
38915         if(Roo.isGecko){// firefox overflow auto bug workaround
38916             this.bodyEl.clip();
38917             if(this.tabs) {
38918                 this.tabs.bodyEl.clip();
38919             }
38920             if(this.activePanel){
38921                 this.activePanel.getEl().clip();
38922                 
38923                 if(this.activePanel.beforeSlide){
38924                     this.activePanel.beforeSlide();
38925                 }
38926             }
38927         }
38928     },
38929     
38930     afterSlide : function(){
38931         if(Roo.isGecko){// firefox overflow auto bug workaround
38932             this.bodyEl.unclip();
38933             if(this.tabs) {
38934                 this.tabs.bodyEl.unclip();
38935             }
38936             if(this.activePanel){
38937                 this.activePanel.getEl().unclip();
38938                 if(this.activePanel.afterSlide){
38939                     this.activePanel.afterSlide();
38940                 }
38941             }
38942         }
38943     },
38944
38945     initAutoHide : function(){
38946         if(this.autoHide !== false){
38947             if(!this.autoHideHd){
38948                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38949                 this.autoHideHd = {
38950                     "mouseout": function(e){
38951                         if(!e.within(this.el, true)){
38952                             st.delay(500);
38953                         }
38954                     },
38955                     "mouseover" : function(e){
38956                         st.cancel();
38957                     },
38958                     scope : this
38959                 };
38960             }
38961             this.el.on(this.autoHideHd);
38962         }
38963     },
38964
38965     clearAutoHide : function(){
38966         if(this.autoHide !== false){
38967             this.el.un("mouseout", this.autoHideHd.mouseout);
38968             this.el.un("mouseover", this.autoHideHd.mouseover);
38969         }
38970     },
38971
38972     clearMonitor : function(){
38973         Roo.get(document).un("click", this.slideInIf, this);
38974     },
38975
38976     // these names are backwards but not changed for compat
38977     slideOut : function(){
38978         if(this.isSlid || this.el.hasActiveFx()){
38979             return;
38980         }
38981         this.isSlid = true;
38982         if(this.collapseBtn){
38983             this.collapseBtn.hide();
38984         }
38985         this.closeBtnState = this.closeBtn.getStyle('display');
38986         this.closeBtn.hide();
38987         if(this.stickBtn){
38988             this.stickBtn.show();
38989         }
38990         this.el.show();
38991         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
38992         this.beforeSlide();
38993         this.el.setStyle("z-index", 10001);
38994         this.el.slideIn(this.getSlideAnchor(), {
38995             callback: function(){
38996                 this.afterSlide();
38997                 this.initAutoHide();
38998                 Roo.get(document).on("click", this.slideInIf, this);
38999                 this.fireEvent("slideshow", this);
39000             },
39001             scope: this,
39002             block: true
39003         });
39004     },
39005
39006     afterSlideIn : function(){
39007         this.clearAutoHide();
39008         this.isSlid = false;
39009         this.clearMonitor();
39010         this.el.setStyle("z-index", "");
39011         if(this.collapseBtn){
39012             this.collapseBtn.show();
39013         }
39014         this.closeBtn.setStyle('display', this.closeBtnState);
39015         if(this.stickBtn){
39016             this.stickBtn.hide();
39017         }
39018         this.fireEvent("slidehide", this);
39019     },
39020
39021     slideIn : function(cb){
39022         if(!this.isSlid || this.el.hasActiveFx()){
39023             Roo.callback(cb);
39024             return;
39025         }
39026         this.isSlid = false;
39027         this.beforeSlide();
39028         this.el.slideOut(this.getSlideAnchor(), {
39029             callback: function(){
39030                 this.el.setLeftTop(-10000, -10000);
39031                 this.afterSlide();
39032                 this.afterSlideIn();
39033                 Roo.callback(cb);
39034             },
39035             scope: this,
39036             block: true
39037         });
39038     },
39039     
39040     slideInIf : function(e){
39041         if(!e.within(this.el)){
39042             this.slideIn();
39043         }
39044     },
39045
39046     animateCollapse : function(){
39047         this.beforeSlide();
39048         this.el.setStyle("z-index", 20000);
39049         var anchor = this.getSlideAnchor();
39050         this.el.slideOut(anchor, {
39051             callback : function(){
39052                 this.el.setStyle("z-index", "");
39053                 this.collapsedEl.slideIn(anchor, {duration:.3});
39054                 this.afterSlide();
39055                 this.el.setLocation(-10000,-10000);
39056                 this.el.hide();
39057                 this.fireEvent("collapsed", this);
39058             },
39059             scope: this,
39060             block: true
39061         });
39062     },
39063
39064     animateExpand : function(){
39065         this.beforeSlide();
39066         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39067         this.el.setStyle("z-index", 20000);
39068         this.collapsedEl.hide({
39069             duration:.1
39070         });
39071         this.el.slideIn(this.getSlideAnchor(), {
39072             callback : function(){
39073                 this.el.setStyle("z-index", "");
39074                 this.afterSlide();
39075                 if(this.split){
39076                     this.split.el.show();
39077                 }
39078                 this.fireEvent("invalidated", this);
39079                 this.fireEvent("expanded", this);
39080             },
39081             scope: this,
39082             block: true
39083         });
39084     },
39085
39086     anchors : {
39087         "west" : "left",
39088         "east" : "right",
39089         "north" : "top",
39090         "south" : "bottom"
39091     },
39092
39093     sanchors : {
39094         "west" : "l",
39095         "east" : "r",
39096         "north" : "t",
39097         "south" : "b"
39098     },
39099
39100     canchors : {
39101         "west" : "tl-tr",
39102         "east" : "tr-tl",
39103         "north" : "tl-bl",
39104         "south" : "bl-tl"
39105     },
39106
39107     getAnchor : function(){
39108         return this.anchors[this.position];
39109     },
39110
39111     getCollapseAnchor : function(){
39112         return this.canchors[this.position];
39113     },
39114
39115     getSlideAnchor : function(){
39116         return this.sanchors[this.position];
39117     },
39118
39119     getAlignAdj : function(){
39120         var cm = this.cmargins;
39121         switch(this.position){
39122             case "west":
39123                 return [0, 0];
39124             break;
39125             case "east":
39126                 return [0, 0];
39127             break;
39128             case "north":
39129                 return [0, 0];
39130             break;
39131             case "south":
39132                 return [0, 0];
39133             break;
39134         }
39135     },
39136
39137     getExpandAdj : function(){
39138         var c = this.collapsedEl, cm = this.cmargins;
39139         switch(this.position){
39140             case "west":
39141                 return [-(cm.right+c.getWidth()+cm.left), 0];
39142             break;
39143             case "east":
39144                 return [cm.right+c.getWidth()+cm.left, 0];
39145             break;
39146             case "north":
39147                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39148             break;
39149             case "south":
39150                 return [0, cm.top+cm.bottom+c.getHeight()];
39151             break;
39152         }
39153     }
39154 });/*
39155  * Based on:
39156  * Ext JS Library 1.1.1
39157  * Copyright(c) 2006-2007, Ext JS, LLC.
39158  *
39159  * Originally Released Under LGPL - original licence link has changed is not relivant.
39160  *
39161  * Fork - LGPL
39162  * <script type="text/javascript">
39163  */
39164 /*
39165  * These classes are private internal classes
39166  */
39167 Roo.bootstrap.layout.Center = function(config){
39168     config.region = "center";
39169     Roo.bootstrap.layout.Region.call(this, config);
39170     this.visible = true;
39171     this.minWidth = config.minWidth || 20;
39172     this.minHeight = config.minHeight || 20;
39173 };
39174
39175 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39176     hide : function(){
39177         // center panel can't be hidden
39178     },
39179     
39180     show : function(){
39181         // center panel can't be hidden
39182     },
39183     
39184     getMinWidth: function(){
39185         return this.minWidth;
39186     },
39187     
39188     getMinHeight: function(){
39189         return this.minHeight;
39190     }
39191 });
39192
39193
39194
39195
39196  
39197
39198
39199
39200
39201
39202
39203 Roo.bootstrap.layout.North = function(config)
39204 {
39205     config.region = 'north';
39206     config.cursor = 'n-resize';
39207     
39208     Roo.bootstrap.layout.Split.call(this, config);
39209     
39210     
39211     if(this.split){
39212         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39213         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39214         this.split.el.addClass("roo-layout-split-v");
39215     }
39216     var size = config.initialSize || config.height;
39217     if(typeof size != "undefined"){
39218         this.el.setHeight(size);
39219     }
39220 };
39221 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39222 {
39223     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39224     
39225     
39226     
39227     getBox : function(){
39228         if(this.collapsed){
39229             return this.collapsedEl.getBox();
39230         }
39231         var box = this.el.getBox();
39232         if(this.split){
39233             box.height += this.split.el.getHeight();
39234         }
39235         return box;
39236     },
39237     
39238     updateBox : function(box){
39239         if(this.split && !this.collapsed){
39240             box.height -= this.split.el.getHeight();
39241             this.split.el.setLeft(box.x);
39242             this.split.el.setTop(box.y+box.height);
39243             this.split.el.setWidth(box.width);
39244         }
39245         if(this.collapsed){
39246             this.updateBody(box.width, null);
39247         }
39248         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39249     }
39250 });
39251
39252
39253
39254
39255
39256 Roo.bootstrap.layout.South = function(config){
39257     config.region = 'south';
39258     config.cursor = 's-resize';
39259     Roo.bootstrap.layout.Split.call(this, config);
39260     if(this.split){
39261         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39262         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39263         this.split.el.addClass("roo-layout-split-v");
39264     }
39265     var size = config.initialSize || config.height;
39266     if(typeof size != "undefined"){
39267         this.el.setHeight(size);
39268     }
39269 };
39270
39271 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39272     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39273     getBox : function(){
39274         if(this.collapsed){
39275             return this.collapsedEl.getBox();
39276         }
39277         var box = this.el.getBox();
39278         if(this.split){
39279             var sh = this.split.el.getHeight();
39280             box.height += sh;
39281             box.y -= sh;
39282         }
39283         return box;
39284     },
39285     
39286     updateBox : function(box){
39287         if(this.split && !this.collapsed){
39288             var sh = this.split.el.getHeight();
39289             box.height -= sh;
39290             box.y += sh;
39291             this.split.el.setLeft(box.x);
39292             this.split.el.setTop(box.y-sh);
39293             this.split.el.setWidth(box.width);
39294         }
39295         if(this.collapsed){
39296             this.updateBody(box.width, null);
39297         }
39298         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39299     }
39300 });
39301
39302 Roo.bootstrap.layout.East = function(config){
39303     config.region = "east";
39304     config.cursor = "e-resize";
39305     Roo.bootstrap.layout.Split.call(this, config);
39306     if(this.split){
39307         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39308         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39309         this.split.el.addClass("roo-layout-split-h");
39310     }
39311     var size = config.initialSize || config.width;
39312     if(typeof size != "undefined"){
39313         this.el.setWidth(size);
39314     }
39315 };
39316 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39317     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39318     getBox : function(){
39319         if(this.collapsed){
39320             return this.collapsedEl.getBox();
39321         }
39322         var box = this.el.getBox();
39323         if(this.split){
39324             var sw = this.split.el.getWidth();
39325             box.width += sw;
39326             box.x -= sw;
39327         }
39328         return box;
39329     },
39330
39331     updateBox : function(box){
39332         if(this.split && !this.collapsed){
39333             var sw = this.split.el.getWidth();
39334             box.width -= sw;
39335             this.split.el.setLeft(box.x);
39336             this.split.el.setTop(box.y);
39337             this.split.el.setHeight(box.height);
39338             box.x += sw;
39339         }
39340         if(this.collapsed){
39341             this.updateBody(null, box.height);
39342         }
39343         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39344     }
39345 });
39346
39347 Roo.bootstrap.layout.West = function(config){
39348     config.region = "west";
39349     config.cursor = "w-resize";
39350     
39351     Roo.bootstrap.layout.Split.call(this, config);
39352     if(this.split){
39353         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39354         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39355         this.split.el.addClass("roo-layout-split-h");
39356     }
39357     
39358 };
39359 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39360     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39361     
39362     onRender: function(ctr, pos)
39363     {
39364         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39365         var size = this.config.initialSize || this.config.width;
39366         if(typeof size != "undefined"){
39367             this.el.setWidth(size);
39368         }
39369     },
39370     
39371     getBox : function(){
39372         if(this.collapsed){
39373             return this.collapsedEl.getBox();
39374         }
39375         var box = this.el.getBox();
39376         if(this.split){
39377             box.width += this.split.el.getWidth();
39378         }
39379         return box;
39380     },
39381     
39382     updateBox : function(box){
39383         if(this.split && !this.collapsed){
39384             var sw = this.split.el.getWidth();
39385             box.width -= sw;
39386             this.split.el.setLeft(box.x+box.width);
39387             this.split.el.setTop(box.y);
39388             this.split.el.setHeight(box.height);
39389         }
39390         if(this.collapsed){
39391             this.updateBody(null, box.height);
39392         }
39393         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39394     }
39395 });Roo.namespace("Roo.bootstrap.panel");/*
39396  * Based on:
39397  * Ext JS Library 1.1.1
39398  * Copyright(c) 2006-2007, Ext JS, LLC.
39399  *
39400  * Originally Released Under LGPL - original licence link has changed is not relivant.
39401  *
39402  * Fork - LGPL
39403  * <script type="text/javascript">
39404  */
39405 /**
39406  * @class Roo.ContentPanel
39407  * @extends Roo.util.Observable
39408  * A basic ContentPanel element.
39409  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39410  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39411  * @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
39412  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39413  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39414  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39415  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39416  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39417  * @cfg {String} title          The title for this panel
39418  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39419  * @cfg {String} url            Calls {@link #setUrl} with this value
39420  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39421  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39422  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39423  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39424  * @cfg {Boolean} badges render the badges
39425  * @cfg {String} cls  extra classes to use  
39426  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39427
39428  * @constructor
39429  * Create a new ContentPanel.
39430  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39431  * @param {String/Object} config A string to set only the title or a config object
39432  * @param {String} content (optional) Set the HTML content for this panel
39433  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39434  */
39435 Roo.bootstrap.panel.Content = function( config){
39436     
39437     this.tpl = config.tpl || false;
39438     
39439     var el = config.el;
39440     var content = config.content;
39441
39442     if(config.autoCreate){ // xtype is available if this is called from factory
39443         el = Roo.id();
39444     }
39445     this.el = Roo.get(el);
39446     if(!this.el && config && config.autoCreate){
39447         if(typeof config.autoCreate == "object"){
39448             if(!config.autoCreate.id){
39449                 config.autoCreate.id = config.id||el;
39450             }
39451             this.el = Roo.DomHelper.append(document.body,
39452                         config.autoCreate, true);
39453         }else{
39454             var elcfg =  {
39455                 tag: "div",
39456                 cls: (config.cls || '') +
39457                     (config.background ? ' bg-' + config.background : '') +
39458                     " roo-layout-inactive-content",
39459                 id: config.id||el
39460             };
39461             if (config.html) {
39462                 elcfg.html = config.html;
39463                 
39464             }
39465                         
39466             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39467         }
39468     } 
39469     this.closable = false;
39470     this.loaded = false;
39471     this.active = false;
39472    
39473       
39474     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39475         
39476         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39477         
39478         this.wrapEl = this.el; //this.el.wrap();
39479         var ti = [];
39480         if (config.toolbar.items) {
39481             ti = config.toolbar.items ;
39482             delete config.toolbar.items ;
39483         }
39484         
39485         var nitems = [];
39486         this.toolbar.render(this.wrapEl, 'before');
39487         for(var i =0;i < ti.length;i++) {
39488           //  Roo.log(['add child', items[i]]);
39489             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39490         }
39491         this.toolbar.items = nitems;
39492         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39493         delete config.toolbar;
39494         
39495     }
39496     /*
39497     // xtype created footer. - not sure if will work as we normally have to render first..
39498     if (this.footer && !this.footer.el && this.footer.xtype) {
39499         if (!this.wrapEl) {
39500             this.wrapEl = this.el.wrap();
39501         }
39502     
39503         this.footer.container = this.wrapEl.createChild();
39504          
39505         this.footer = Roo.factory(this.footer, Roo);
39506         
39507     }
39508     */
39509     
39510      if(typeof config == "string"){
39511         this.title = config;
39512     }else{
39513         Roo.apply(this, config);
39514     }
39515     
39516     if(this.resizeEl){
39517         this.resizeEl = Roo.get(this.resizeEl, true);
39518     }else{
39519         this.resizeEl = this.el;
39520     }
39521     // handle view.xtype
39522     
39523  
39524     
39525     
39526     this.addEvents({
39527         /**
39528          * @event activate
39529          * Fires when this panel is activated. 
39530          * @param {Roo.ContentPanel} this
39531          */
39532         "activate" : true,
39533         /**
39534          * @event deactivate
39535          * Fires when this panel is activated. 
39536          * @param {Roo.ContentPanel} this
39537          */
39538         "deactivate" : true,
39539
39540         /**
39541          * @event resize
39542          * Fires when this panel is resized if fitToFrame is true.
39543          * @param {Roo.ContentPanel} this
39544          * @param {Number} width The width after any component adjustments
39545          * @param {Number} height The height after any component adjustments
39546          */
39547         "resize" : true,
39548         
39549          /**
39550          * @event render
39551          * Fires when this tab is created
39552          * @param {Roo.ContentPanel} this
39553          */
39554         "render" : true
39555         
39556         
39557         
39558     });
39559     
39560
39561     
39562     
39563     if(this.autoScroll){
39564         this.resizeEl.setStyle("overflow", "auto");
39565     } else {
39566         // fix randome scrolling
39567         //this.el.on('scroll', function() {
39568         //    Roo.log('fix random scolling');
39569         //    this.scrollTo('top',0); 
39570         //});
39571     }
39572     content = content || this.content;
39573     if(content){
39574         this.setContent(content);
39575     }
39576     if(config && config.url){
39577         this.setUrl(this.url, this.params, this.loadOnce);
39578     }
39579     
39580     
39581     
39582     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39583     
39584     if (this.view && typeof(this.view.xtype) != 'undefined') {
39585         this.view.el = this.el.appendChild(document.createElement("div"));
39586         this.view = Roo.factory(this.view); 
39587         this.view.render  &&  this.view.render(false, '');  
39588     }
39589     
39590     
39591     this.fireEvent('render', this);
39592 };
39593
39594 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39595     
39596     cls : '',
39597     background : '',
39598     
39599     tabTip : '',
39600     
39601     setRegion : function(region){
39602         this.region = region;
39603         this.setActiveClass(region && !this.background);
39604     },
39605     
39606     
39607     setActiveClass: function(state)
39608     {
39609         if(state){
39610            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39611            this.el.setStyle('position','relative');
39612         }else{
39613            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39614            this.el.setStyle('position', 'absolute');
39615         } 
39616     },
39617     
39618     /**
39619      * Returns the toolbar for this Panel if one was configured. 
39620      * @return {Roo.Toolbar} 
39621      */
39622     getToolbar : function(){
39623         return this.toolbar;
39624     },
39625     
39626     setActiveState : function(active)
39627     {
39628         this.active = active;
39629         this.setActiveClass(active);
39630         if(!active){
39631             if(this.fireEvent("deactivate", this) === false){
39632                 return false;
39633             }
39634             return true;
39635         }
39636         this.fireEvent("activate", this);
39637         return true;
39638     },
39639     /**
39640      * Updates this panel's element
39641      * @param {String} content The new content
39642      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39643     */
39644     setContent : function(content, loadScripts){
39645         this.el.update(content, loadScripts);
39646     },
39647
39648     ignoreResize : function(w, h){
39649         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39650             return true;
39651         }else{
39652             this.lastSize = {width: w, height: h};
39653             return false;
39654         }
39655     },
39656     /**
39657      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39658      * @return {Roo.UpdateManager} The UpdateManager
39659      */
39660     getUpdateManager : function(){
39661         return this.el.getUpdateManager();
39662     },
39663      /**
39664      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39665      * @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:
39666 <pre><code>
39667 panel.load({
39668     url: "your-url.php",
39669     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39670     callback: yourFunction,
39671     scope: yourObject, //(optional scope)
39672     discardUrl: false,
39673     nocache: false,
39674     text: "Loading...",
39675     timeout: 30,
39676     scripts: false
39677 });
39678 </code></pre>
39679      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39680      * 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.
39681      * @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}
39682      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39683      * @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.
39684      * @return {Roo.ContentPanel} this
39685      */
39686     load : function(){
39687         var um = this.el.getUpdateManager();
39688         um.update.apply(um, arguments);
39689         return this;
39690     },
39691
39692
39693     /**
39694      * 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.
39695      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39696      * @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)
39697      * @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)
39698      * @return {Roo.UpdateManager} The UpdateManager
39699      */
39700     setUrl : function(url, params, loadOnce){
39701         if(this.refreshDelegate){
39702             this.removeListener("activate", this.refreshDelegate);
39703         }
39704         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39705         this.on("activate", this.refreshDelegate);
39706         return this.el.getUpdateManager();
39707     },
39708     
39709     _handleRefresh : function(url, params, loadOnce){
39710         if(!loadOnce || !this.loaded){
39711             var updater = this.el.getUpdateManager();
39712             updater.update(url, params, this._setLoaded.createDelegate(this));
39713         }
39714     },
39715     
39716     _setLoaded : function(){
39717         this.loaded = true;
39718     }, 
39719     
39720     /**
39721      * Returns this panel's id
39722      * @return {String} 
39723      */
39724     getId : function(){
39725         return this.el.id;
39726     },
39727     
39728     /** 
39729      * Returns this panel's element - used by regiosn to add.
39730      * @return {Roo.Element} 
39731      */
39732     getEl : function(){
39733         return this.wrapEl || this.el;
39734     },
39735     
39736    
39737     
39738     adjustForComponents : function(width, height)
39739     {
39740         //Roo.log('adjustForComponents ');
39741         if(this.resizeEl != this.el){
39742             width -= this.el.getFrameWidth('lr');
39743             height -= this.el.getFrameWidth('tb');
39744         }
39745         if(this.toolbar){
39746             var te = this.toolbar.getEl();
39747             te.setWidth(width);
39748             height -= te.getHeight();
39749         }
39750         if(this.footer){
39751             var te = this.footer.getEl();
39752             te.setWidth(width);
39753             height -= te.getHeight();
39754         }
39755         
39756         
39757         if(this.adjustments){
39758             width += this.adjustments[0];
39759             height += this.adjustments[1];
39760         }
39761         return {"width": width, "height": height};
39762     },
39763     
39764     setSize : function(width, height){
39765         if(this.fitToFrame && !this.ignoreResize(width, height)){
39766             if(this.fitContainer && this.resizeEl != this.el){
39767                 this.el.setSize(width, height);
39768             }
39769             var size = this.adjustForComponents(width, height);
39770             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39771             this.fireEvent('resize', this, size.width, size.height);
39772         }
39773     },
39774     
39775     /**
39776      * Returns this panel's title
39777      * @return {String} 
39778      */
39779     getTitle : function(){
39780         
39781         if (typeof(this.title) != 'object') {
39782             return this.title;
39783         }
39784         
39785         var t = '';
39786         for (var k in this.title) {
39787             if (!this.title.hasOwnProperty(k)) {
39788                 continue;
39789             }
39790             
39791             if (k.indexOf('-') >= 0) {
39792                 var s = k.split('-');
39793                 for (var i = 0; i<s.length; i++) {
39794                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39795                 }
39796             } else {
39797                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39798             }
39799         }
39800         return t;
39801     },
39802     
39803     /**
39804      * Set this panel's title
39805      * @param {String} title
39806      */
39807     setTitle : function(title){
39808         this.title = title;
39809         if(this.region){
39810             this.region.updatePanelTitle(this, title);
39811         }
39812     },
39813     
39814     /**
39815      * Returns true is this panel was configured to be closable
39816      * @return {Boolean} 
39817      */
39818     isClosable : function(){
39819         return this.closable;
39820     },
39821     
39822     beforeSlide : function(){
39823         this.el.clip();
39824         this.resizeEl.clip();
39825     },
39826     
39827     afterSlide : function(){
39828         this.el.unclip();
39829         this.resizeEl.unclip();
39830     },
39831     
39832     /**
39833      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39834      *   Will fail silently if the {@link #setUrl} method has not been called.
39835      *   This does not activate the panel, just updates its content.
39836      */
39837     refresh : function(){
39838         if(this.refreshDelegate){
39839            this.loaded = false;
39840            this.refreshDelegate();
39841         }
39842     },
39843     
39844     /**
39845      * Destroys this panel
39846      */
39847     destroy : function(){
39848         this.el.removeAllListeners();
39849         var tempEl = document.createElement("span");
39850         tempEl.appendChild(this.el.dom);
39851         tempEl.innerHTML = "";
39852         this.el.remove();
39853         this.el = null;
39854     },
39855     
39856     /**
39857      * form - if the content panel contains a form - this is a reference to it.
39858      * @type {Roo.form.Form}
39859      */
39860     form : false,
39861     /**
39862      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39863      *    This contains a reference to it.
39864      * @type {Roo.View}
39865      */
39866     view : false,
39867     
39868       /**
39869      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39870      * <pre><code>
39871
39872 layout.addxtype({
39873        xtype : 'Form',
39874        items: [ .... ]
39875    }
39876 );
39877
39878 </code></pre>
39879      * @param {Object} cfg Xtype definition of item to add.
39880      */
39881     
39882     
39883     getChildContainer: function () {
39884         return this.getEl();
39885     }
39886     
39887     
39888     /*
39889         var  ret = new Roo.factory(cfg);
39890         return ret;
39891         
39892         
39893         // add form..
39894         if (cfg.xtype.match(/^Form$/)) {
39895             
39896             var el;
39897             //if (this.footer) {
39898             //    el = this.footer.container.insertSibling(false, 'before');
39899             //} else {
39900                 el = this.el.createChild();
39901             //}
39902
39903             this.form = new  Roo.form.Form(cfg);
39904             
39905             
39906             if ( this.form.allItems.length) {
39907                 this.form.render(el.dom);
39908             }
39909             return this.form;
39910         }
39911         // should only have one of theses..
39912         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39913             // views.. should not be just added - used named prop 'view''
39914             
39915             cfg.el = this.el.appendChild(document.createElement("div"));
39916             // factory?
39917             
39918             var ret = new Roo.factory(cfg);
39919              
39920              ret.render && ret.render(false, ''); // render blank..
39921             this.view = ret;
39922             return ret;
39923         }
39924         return false;
39925     }
39926     \*/
39927 });
39928  
39929 /**
39930  * @class Roo.bootstrap.panel.Grid
39931  * @extends Roo.bootstrap.panel.Content
39932  * @constructor
39933  * Create a new GridPanel.
39934  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39935  * @param {Object} config A the config object
39936   
39937  */
39938
39939
39940
39941 Roo.bootstrap.panel.Grid = function(config)
39942 {
39943     
39944       
39945     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39946         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39947
39948     config.el = this.wrapper;
39949     //this.el = this.wrapper;
39950     
39951       if (config.container) {
39952         // ctor'ed from a Border/panel.grid
39953         
39954         
39955         this.wrapper.setStyle("overflow", "hidden");
39956         this.wrapper.addClass('roo-grid-container');
39957
39958     }
39959     
39960     
39961     if(config.toolbar){
39962         var tool_el = this.wrapper.createChild();    
39963         this.toolbar = Roo.factory(config.toolbar);
39964         var ti = [];
39965         if (config.toolbar.items) {
39966             ti = config.toolbar.items ;
39967             delete config.toolbar.items ;
39968         }
39969         
39970         var nitems = [];
39971         this.toolbar.render(tool_el);
39972         for(var i =0;i < ti.length;i++) {
39973           //  Roo.log(['add child', items[i]]);
39974             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39975         }
39976         this.toolbar.items = nitems;
39977         
39978         delete config.toolbar;
39979     }
39980     
39981     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39982     config.grid.scrollBody = true;;
39983     config.grid.monitorWindowResize = false; // turn off autosizing
39984     config.grid.autoHeight = false;
39985     config.grid.autoWidth = false;
39986     
39987     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39988     
39989     if (config.background) {
39990         // render grid on panel activation (if panel background)
39991         this.on('activate', function(gp) {
39992             if (!gp.grid.rendered) {
39993                 gp.grid.render(this.wrapper);
39994                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
39995             }
39996         });
39997             
39998     } else {
39999         this.grid.render(this.wrapper);
40000         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40001
40002     }
40003     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40004     // ??? needed ??? config.el = this.wrapper;
40005     
40006     
40007     
40008   
40009     // xtype created footer. - not sure if will work as we normally have to render first..
40010     if (this.footer && !this.footer.el && this.footer.xtype) {
40011         
40012         var ctr = this.grid.getView().getFooterPanel(true);
40013         this.footer.dataSource = this.grid.dataSource;
40014         this.footer = Roo.factory(this.footer, Roo);
40015         this.footer.render(ctr);
40016         
40017     }
40018     
40019     
40020     
40021     
40022      
40023 };
40024
40025 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40026     getId : function(){
40027         return this.grid.id;
40028     },
40029     
40030     /**
40031      * Returns the grid for this panel
40032      * @return {Roo.bootstrap.Table} 
40033      */
40034     getGrid : function(){
40035         return this.grid;    
40036     },
40037     
40038     setSize : function(width, height){
40039         if(!this.ignoreResize(width, height)){
40040             var grid = this.grid;
40041             var size = this.adjustForComponents(width, height);
40042             // tfoot is not a footer?
40043           
40044             
40045             var gridel = grid.getGridEl();
40046             gridel.setSize(size.width, size.height);
40047             
40048             var tbd = grid.getGridEl().select('tbody', true).first();
40049             var thd = grid.getGridEl().select('thead',true).first();
40050             var tbf= grid.getGridEl().select('tfoot', true).first();
40051
40052             if (tbf) {
40053                 size.height -= thd.getHeight();
40054             }
40055             if (thd) {
40056                 size.height -= thd.getHeight();
40057             }
40058             
40059             tbd.setSize(size.width, size.height );
40060             // this is for the account management tab -seems to work there.
40061             var thd = grid.getGridEl().select('thead',true).first();
40062             //if (tbd) {
40063             //    tbd.setSize(size.width, size.height - thd.getHeight());
40064             //}
40065              
40066             grid.autoSize();
40067         }
40068     },
40069      
40070     
40071     
40072     beforeSlide : function(){
40073         this.grid.getView().scroller.clip();
40074     },
40075     
40076     afterSlide : function(){
40077         this.grid.getView().scroller.unclip();
40078     },
40079     
40080     destroy : function(){
40081         this.grid.destroy();
40082         delete this.grid;
40083         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40084     }
40085 });
40086
40087 /**
40088  * @class Roo.bootstrap.panel.Nest
40089  * @extends Roo.bootstrap.panel.Content
40090  * @constructor
40091  * Create a new Panel, that can contain a layout.Border.
40092  * 
40093  * 
40094  * @param {Roo.BorderLayout} layout The layout for this panel
40095  * @param {String/Object} config A string to set only the title or a config object
40096  */
40097 Roo.bootstrap.panel.Nest = function(config)
40098 {
40099     // construct with only one argument..
40100     /* FIXME - implement nicer consturctors
40101     if (layout.layout) {
40102         config = layout;
40103         layout = config.layout;
40104         delete config.layout;
40105     }
40106     if (layout.xtype && !layout.getEl) {
40107         // then layout needs constructing..
40108         layout = Roo.factory(layout, Roo);
40109     }
40110     */
40111     
40112     config.el =  config.layout.getEl();
40113     
40114     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40115     
40116     config.layout.monitorWindowResize = false; // turn off autosizing
40117     this.layout = config.layout;
40118     this.layout.getEl().addClass("roo-layout-nested-layout");
40119     this.layout.parent = this;
40120     
40121     
40122     
40123     
40124 };
40125
40126 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40127
40128     setSize : function(width, height){
40129         if(!this.ignoreResize(width, height)){
40130             var size = this.adjustForComponents(width, height);
40131             var el = this.layout.getEl();
40132             if (size.height < 1) {
40133                 el.setWidth(size.width);   
40134             } else {
40135                 el.setSize(size.width, size.height);
40136             }
40137             var touch = el.dom.offsetWidth;
40138             this.layout.layout();
40139             // ie requires a double layout on the first pass
40140             if(Roo.isIE && !this.initialized){
40141                 this.initialized = true;
40142                 this.layout.layout();
40143             }
40144         }
40145     },
40146     
40147     // activate all subpanels if not currently active..
40148     
40149     setActiveState : function(active){
40150         this.active = active;
40151         this.setActiveClass(active);
40152         
40153         if(!active){
40154             this.fireEvent("deactivate", this);
40155             return;
40156         }
40157         
40158         this.fireEvent("activate", this);
40159         // not sure if this should happen before or after..
40160         if (!this.layout) {
40161             return; // should not happen..
40162         }
40163         var reg = false;
40164         for (var r in this.layout.regions) {
40165             reg = this.layout.getRegion(r);
40166             if (reg.getActivePanel()) {
40167                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40168                 reg.setActivePanel(reg.getActivePanel());
40169                 continue;
40170             }
40171             if (!reg.panels.length) {
40172                 continue;
40173             }
40174             reg.showPanel(reg.getPanel(0));
40175         }
40176         
40177         
40178         
40179         
40180     },
40181     
40182     /**
40183      * Returns the nested BorderLayout for this panel
40184      * @return {Roo.BorderLayout} 
40185      */
40186     getLayout : function(){
40187         return this.layout;
40188     },
40189     
40190      /**
40191      * Adds a xtype elements to the layout of the nested panel
40192      * <pre><code>
40193
40194 panel.addxtype({
40195        xtype : 'ContentPanel',
40196        region: 'west',
40197        items: [ .... ]
40198    }
40199 );
40200
40201 panel.addxtype({
40202         xtype : 'NestedLayoutPanel',
40203         region: 'west',
40204         layout: {
40205            center: { },
40206            west: { }   
40207         },
40208         items : [ ... list of content panels or nested layout panels.. ]
40209    }
40210 );
40211 </code></pre>
40212      * @param {Object} cfg Xtype definition of item to add.
40213      */
40214     addxtype : function(cfg) {
40215         return this.layout.addxtype(cfg);
40216     
40217     }
40218 });/*
40219  * Based on:
40220  * Ext JS Library 1.1.1
40221  * Copyright(c) 2006-2007, Ext JS, LLC.
40222  *
40223  * Originally Released Under LGPL - original licence link has changed is not relivant.
40224  *
40225  * Fork - LGPL
40226  * <script type="text/javascript">
40227  */
40228 /**
40229  * @class Roo.TabPanel
40230  * @extends Roo.util.Observable
40231  * A lightweight tab container.
40232  * <br><br>
40233  * Usage:
40234  * <pre><code>
40235 // basic tabs 1, built from existing content
40236 var tabs = new Roo.TabPanel("tabs1");
40237 tabs.addTab("script", "View Script");
40238 tabs.addTab("markup", "View Markup");
40239 tabs.activate("script");
40240
40241 // more advanced tabs, built from javascript
40242 var jtabs = new Roo.TabPanel("jtabs");
40243 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40244
40245 // set up the UpdateManager
40246 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40247 var updater = tab2.getUpdateManager();
40248 updater.setDefaultUrl("ajax1.htm");
40249 tab2.on('activate', updater.refresh, updater, true);
40250
40251 // Use setUrl for Ajax loading
40252 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40253 tab3.setUrl("ajax2.htm", null, true);
40254
40255 // Disabled tab
40256 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40257 tab4.disable();
40258
40259 jtabs.activate("jtabs-1");
40260  * </code></pre>
40261  * @constructor
40262  * Create a new TabPanel.
40263  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40264  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40265  */
40266 Roo.bootstrap.panel.Tabs = function(config){
40267     /**
40268     * The container element for this TabPanel.
40269     * @type Roo.Element
40270     */
40271     this.el = Roo.get(config.el);
40272     delete config.el;
40273     if(config){
40274         if(typeof config == "boolean"){
40275             this.tabPosition = config ? "bottom" : "top";
40276         }else{
40277             Roo.apply(this, config);
40278         }
40279     }
40280     
40281     if(this.tabPosition == "bottom"){
40282         // if tabs are at the bottom = create the body first.
40283         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40284         this.el.addClass("roo-tabs-bottom");
40285     }
40286     // next create the tabs holders
40287     
40288     if (this.tabPosition == "west"){
40289         
40290         var reg = this.region; // fake it..
40291         while (reg) {
40292             if (!reg.mgr.parent) {
40293                 break;
40294             }
40295             reg = reg.mgr.parent.region;
40296         }
40297         Roo.log("got nest?");
40298         Roo.log(reg);
40299         if (reg.mgr.getRegion('west')) {
40300             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40301             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40302             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40303             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40304             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40305         
40306             
40307         }
40308         
40309         
40310     } else {
40311      
40312         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40313         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40314         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40315         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40316     }
40317     
40318     
40319     if(Roo.isIE){
40320         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40321     }
40322     
40323     // finally - if tabs are at the top, then create the body last..
40324     if(this.tabPosition != "bottom"){
40325         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40326          * @type Roo.Element
40327          */
40328         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40329         this.el.addClass("roo-tabs-top");
40330     }
40331     this.items = [];
40332
40333     this.bodyEl.setStyle("position", "relative");
40334
40335     this.active = null;
40336     this.activateDelegate = this.activate.createDelegate(this);
40337
40338     this.addEvents({
40339         /**
40340          * @event tabchange
40341          * Fires when the active tab changes
40342          * @param {Roo.TabPanel} this
40343          * @param {Roo.TabPanelItem} activePanel The new active tab
40344          */
40345         "tabchange": true,
40346         /**
40347          * @event beforetabchange
40348          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40349          * @param {Roo.TabPanel} this
40350          * @param {Object} e Set cancel to true on this object to cancel the tab change
40351          * @param {Roo.TabPanelItem} tab The tab being changed to
40352          */
40353         "beforetabchange" : true
40354     });
40355
40356     Roo.EventManager.onWindowResize(this.onResize, this);
40357     this.cpad = this.el.getPadding("lr");
40358     this.hiddenCount = 0;
40359
40360
40361     // toolbar on the tabbar support...
40362     if (this.toolbar) {
40363         alert("no toolbar support yet");
40364         this.toolbar  = false;
40365         /*
40366         var tcfg = this.toolbar;
40367         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40368         this.toolbar = new Roo.Toolbar(tcfg);
40369         if (Roo.isSafari) {
40370             var tbl = tcfg.container.child('table', true);
40371             tbl.setAttribute('width', '100%');
40372         }
40373         */
40374         
40375     }
40376    
40377
40378
40379     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40380 };
40381
40382 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40383     /*
40384      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40385      */
40386     tabPosition : "top",
40387     /*
40388      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40389      */
40390     currentTabWidth : 0,
40391     /*
40392      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40393      */
40394     minTabWidth : 40,
40395     /*
40396      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40397      */
40398     maxTabWidth : 250,
40399     /*
40400      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40401      */
40402     preferredTabWidth : 175,
40403     /*
40404      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40405      */
40406     resizeTabs : false,
40407     /*
40408      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40409      */
40410     monitorResize : true,
40411     /*
40412      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40413      */
40414     toolbar : false,  // set by caller..
40415     
40416     region : false, /// set by caller
40417     
40418     disableTooltips : true, // not used yet...
40419
40420     /**
40421      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40422      * @param {String} id The id of the div to use <b>or create</b>
40423      * @param {String} text The text for the tab
40424      * @param {String} content (optional) Content to put in the TabPanelItem body
40425      * @param {Boolean} closable (optional) True to create a close icon on the tab
40426      * @return {Roo.TabPanelItem} The created TabPanelItem
40427      */
40428     addTab : function(id, text, content, closable, tpl)
40429     {
40430         var item = new Roo.bootstrap.panel.TabItem({
40431             panel: this,
40432             id : id,
40433             text : text,
40434             closable : closable,
40435             tpl : tpl
40436         });
40437         this.addTabItem(item);
40438         if(content){
40439             item.setContent(content);
40440         }
40441         return item;
40442     },
40443
40444     /**
40445      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40446      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40447      * @return {Roo.TabPanelItem}
40448      */
40449     getTab : function(id){
40450         return this.items[id];
40451     },
40452
40453     /**
40454      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40455      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40456      */
40457     hideTab : function(id){
40458         var t = this.items[id];
40459         if(!t.isHidden()){
40460            t.setHidden(true);
40461            this.hiddenCount++;
40462            this.autoSizeTabs();
40463         }
40464     },
40465
40466     /**
40467      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40468      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40469      */
40470     unhideTab : function(id){
40471         var t = this.items[id];
40472         if(t.isHidden()){
40473            t.setHidden(false);
40474            this.hiddenCount--;
40475            this.autoSizeTabs();
40476         }
40477     },
40478
40479     /**
40480      * Adds an existing {@link Roo.TabPanelItem}.
40481      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40482      */
40483     addTabItem : function(item)
40484     {
40485         this.items[item.id] = item;
40486         this.items.push(item);
40487         this.autoSizeTabs();
40488       //  if(this.resizeTabs){
40489     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40490   //         this.autoSizeTabs();
40491 //        }else{
40492 //            item.autoSize();
40493        // }
40494     },
40495
40496     /**
40497      * Removes a {@link Roo.TabPanelItem}.
40498      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40499      */
40500     removeTab : function(id){
40501         var items = this.items;
40502         var tab = items[id];
40503         if(!tab) { return; }
40504         var index = items.indexOf(tab);
40505         if(this.active == tab && items.length > 1){
40506             var newTab = this.getNextAvailable(index);
40507             if(newTab) {
40508                 newTab.activate();
40509             }
40510         }
40511         this.stripEl.dom.removeChild(tab.pnode.dom);
40512         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40513             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40514         }
40515         items.splice(index, 1);
40516         delete this.items[tab.id];
40517         tab.fireEvent("close", tab);
40518         tab.purgeListeners();
40519         this.autoSizeTabs();
40520     },
40521
40522     getNextAvailable : function(start){
40523         var items = this.items;
40524         var index = start;
40525         // look for a next tab that will slide over to
40526         // replace the one being removed
40527         while(index < items.length){
40528             var item = items[++index];
40529             if(item && !item.isHidden()){
40530                 return item;
40531             }
40532         }
40533         // if one isn't found select the previous tab (on the left)
40534         index = start;
40535         while(index >= 0){
40536             var item = items[--index];
40537             if(item && !item.isHidden()){
40538                 return item;
40539             }
40540         }
40541         return null;
40542     },
40543
40544     /**
40545      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40546      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40547      */
40548     disableTab : function(id){
40549         var tab = this.items[id];
40550         if(tab && this.active != tab){
40551             tab.disable();
40552         }
40553     },
40554
40555     /**
40556      * Enables a {@link Roo.TabPanelItem} that is disabled.
40557      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40558      */
40559     enableTab : function(id){
40560         var tab = this.items[id];
40561         tab.enable();
40562     },
40563
40564     /**
40565      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40566      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40567      * @return {Roo.TabPanelItem} The TabPanelItem.
40568      */
40569     activate : function(id)
40570     {
40571         //Roo.log('activite:'  + id);
40572         
40573         var tab = this.items[id];
40574         if(!tab){
40575             return null;
40576         }
40577         if(tab == this.active || tab.disabled){
40578             return tab;
40579         }
40580         var e = {};
40581         this.fireEvent("beforetabchange", this, e, tab);
40582         if(e.cancel !== true && !tab.disabled){
40583             if(this.active){
40584                 this.active.hide();
40585             }
40586             this.active = this.items[id];
40587             this.active.show();
40588             this.fireEvent("tabchange", this, this.active);
40589         }
40590         return tab;
40591     },
40592
40593     /**
40594      * Gets the active {@link Roo.TabPanelItem}.
40595      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40596      */
40597     getActiveTab : function(){
40598         return this.active;
40599     },
40600
40601     /**
40602      * Updates the tab body element to fit the height of the container element
40603      * for overflow scrolling
40604      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40605      */
40606     syncHeight : function(targetHeight){
40607         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40608         var bm = this.bodyEl.getMargins();
40609         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40610         this.bodyEl.setHeight(newHeight);
40611         return newHeight;
40612     },
40613
40614     onResize : function(){
40615         if(this.monitorResize){
40616             this.autoSizeTabs();
40617         }
40618     },
40619
40620     /**
40621      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40622      */
40623     beginUpdate : function(){
40624         this.updating = true;
40625     },
40626
40627     /**
40628      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40629      */
40630     endUpdate : function(){
40631         this.updating = false;
40632         this.autoSizeTabs();
40633     },
40634
40635     /**
40636      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40637      */
40638     autoSizeTabs : function()
40639     {
40640         var count = this.items.length;
40641         var vcount = count - this.hiddenCount;
40642         
40643         if (vcount < 2) {
40644             this.stripEl.hide();
40645         } else {
40646             this.stripEl.show();
40647         }
40648         
40649         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40650             return;
40651         }
40652         
40653         
40654         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40655         var availWidth = Math.floor(w / vcount);
40656         var b = this.stripBody;
40657         if(b.getWidth() > w){
40658             var tabs = this.items;
40659             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40660             if(availWidth < this.minTabWidth){
40661                 /*if(!this.sleft){    // incomplete scrolling code
40662                     this.createScrollButtons();
40663                 }
40664                 this.showScroll();
40665                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40666             }
40667         }else{
40668             if(this.currentTabWidth < this.preferredTabWidth){
40669                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40670             }
40671         }
40672     },
40673
40674     /**
40675      * Returns the number of tabs in this TabPanel.
40676      * @return {Number}
40677      */
40678      getCount : function(){
40679          return this.items.length;
40680      },
40681
40682     /**
40683      * Resizes all the tabs to the passed width
40684      * @param {Number} The new width
40685      */
40686     setTabWidth : function(width){
40687         this.currentTabWidth = width;
40688         for(var i = 0, len = this.items.length; i < len; i++) {
40689                 if(!this.items[i].isHidden()) {
40690                 this.items[i].setWidth(width);
40691             }
40692         }
40693     },
40694
40695     /**
40696      * Destroys this TabPanel
40697      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40698      */
40699     destroy : function(removeEl){
40700         Roo.EventManager.removeResizeListener(this.onResize, this);
40701         for(var i = 0, len = this.items.length; i < len; i++){
40702             this.items[i].purgeListeners();
40703         }
40704         if(removeEl === true){
40705             this.el.update("");
40706             this.el.remove();
40707         }
40708     },
40709     
40710     createStrip : function(container)
40711     {
40712         var strip = document.createElement("nav");
40713         strip.className = Roo.bootstrap.version == 4 ?
40714             "navbar-light bg-light" : 
40715             "navbar navbar-default"; //"x-tabs-wrap";
40716         container.appendChild(strip);
40717         return strip;
40718     },
40719     
40720     createStripList : function(strip)
40721     {
40722         // div wrapper for retard IE
40723         // returns the "tr" element.
40724         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40725         //'<div class="x-tabs-strip-wrap">'+
40726           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40727           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40728         return strip.firstChild; //.firstChild.firstChild.firstChild;
40729     },
40730     createBody : function(container)
40731     {
40732         var body = document.createElement("div");
40733         Roo.id(body, "tab-body");
40734         //Roo.fly(body).addClass("x-tabs-body");
40735         Roo.fly(body).addClass("tab-content");
40736         container.appendChild(body);
40737         return body;
40738     },
40739     createItemBody :function(bodyEl, id){
40740         var body = Roo.getDom(id);
40741         if(!body){
40742             body = document.createElement("div");
40743             body.id = id;
40744         }
40745         //Roo.fly(body).addClass("x-tabs-item-body");
40746         Roo.fly(body).addClass("tab-pane");
40747          bodyEl.insertBefore(body, bodyEl.firstChild);
40748         return body;
40749     },
40750     /** @private */
40751     createStripElements :  function(stripEl, text, closable, tpl)
40752     {
40753         var td = document.createElement("li"); // was td..
40754         td.className = 'nav-item';
40755         
40756         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40757         
40758         
40759         stripEl.appendChild(td);
40760         /*if(closable){
40761             td.className = "x-tabs-closable";
40762             if(!this.closeTpl){
40763                 this.closeTpl = new Roo.Template(
40764                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40765                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40766                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40767                 );
40768             }
40769             var el = this.closeTpl.overwrite(td, {"text": text});
40770             var close = el.getElementsByTagName("div")[0];
40771             var inner = el.getElementsByTagName("em")[0];
40772             return {"el": el, "close": close, "inner": inner};
40773         } else {
40774         */
40775         // not sure what this is..
40776 //            if(!this.tabTpl){
40777                 //this.tabTpl = new Roo.Template(
40778                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40779                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40780                 //);
40781 //                this.tabTpl = new Roo.Template(
40782 //                   '<a href="#">' +
40783 //                   '<span unselectable="on"' +
40784 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40785 //                            ' >{text}</span></a>'
40786 //                );
40787 //                
40788 //            }
40789
40790
40791             var template = tpl || this.tabTpl || false;
40792             
40793             if(!template){
40794                 template =  new Roo.Template(
40795                         Roo.bootstrap.version == 4 ? 
40796                             (
40797                                 '<a class="nav-link" href="#" unselectable="on"' +
40798                                      (this.disableTooltips ? '' : ' title="{text}"') +
40799                                      ' >{text}</a>'
40800                             ) : (
40801                                 '<a class="nav-link" href="#">' +
40802                                 '<span unselectable="on"' +
40803                                          (this.disableTooltips ? '' : ' title="{text}"') +
40804                                     ' >{text}</span></a>'
40805                             )
40806                 );
40807             }
40808             
40809             switch (typeof(template)) {
40810                 case 'object' :
40811                     break;
40812                 case 'string' :
40813                     template = new Roo.Template(template);
40814                     break;
40815                 default :
40816                     break;
40817             }
40818             
40819             var el = template.overwrite(td, {"text": text});
40820             
40821             var inner = el.getElementsByTagName("span")[0];
40822             
40823             return {"el": el, "inner": inner};
40824             
40825     }
40826         
40827     
40828 });
40829
40830 /**
40831  * @class Roo.TabPanelItem
40832  * @extends Roo.util.Observable
40833  * Represents an individual item (tab plus body) in a TabPanel.
40834  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40835  * @param {String} id The id of this TabPanelItem
40836  * @param {String} text The text for the tab of this TabPanelItem
40837  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40838  */
40839 Roo.bootstrap.panel.TabItem = function(config){
40840     /**
40841      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40842      * @type Roo.TabPanel
40843      */
40844     this.tabPanel = config.panel;
40845     /**
40846      * The id for this TabPanelItem
40847      * @type String
40848      */
40849     this.id = config.id;
40850     /** @private */
40851     this.disabled = false;
40852     /** @private */
40853     this.text = config.text;
40854     /** @private */
40855     this.loaded = false;
40856     this.closable = config.closable;
40857
40858     /**
40859      * The body element for this TabPanelItem.
40860      * @type Roo.Element
40861      */
40862     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40863     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40864     this.bodyEl.setStyle("display", "block");
40865     this.bodyEl.setStyle("zoom", "1");
40866     //this.hideAction();
40867
40868     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40869     /** @private */
40870     this.el = Roo.get(els.el);
40871     this.inner = Roo.get(els.inner, true);
40872      this.textEl = Roo.bootstrap.version == 4 ?
40873         this.el : Roo.get(this.el.dom.firstChild, true);
40874
40875     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40876     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40877
40878     
40879 //    this.el.on("mousedown", this.onTabMouseDown, this);
40880     this.el.on("click", this.onTabClick, this);
40881     /** @private */
40882     if(config.closable){
40883         var c = Roo.get(els.close, true);
40884         c.dom.title = this.closeText;
40885         c.addClassOnOver("close-over");
40886         c.on("click", this.closeClick, this);
40887      }
40888
40889     this.addEvents({
40890          /**
40891          * @event activate
40892          * Fires when this tab becomes the active tab.
40893          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40894          * @param {Roo.TabPanelItem} this
40895          */
40896         "activate": true,
40897         /**
40898          * @event beforeclose
40899          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40900          * @param {Roo.TabPanelItem} this
40901          * @param {Object} e Set cancel to true on this object to cancel the close.
40902          */
40903         "beforeclose": true,
40904         /**
40905          * @event close
40906          * Fires when this tab is closed.
40907          * @param {Roo.TabPanelItem} this
40908          */
40909          "close": true,
40910         /**
40911          * @event deactivate
40912          * Fires when this tab is no longer the active tab.
40913          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40914          * @param {Roo.TabPanelItem} this
40915          */
40916          "deactivate" : true
40917     });
40918     this.hidden = false;
40919
40920     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40921 };
40922
40923 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40924            {
40925     purgeListeners : function(){
40926        Roo.util.Observable.prototype.purgeListeners.call(this);
40927        this.el.removeAllListeners();
40928     },
40929     /**
40930      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40931      */
40932     show : function(){
40933         this.status_node.addClass("active");
40934         this.showAction();
40935         if(Roo.isOpera){
40936             this.tabPanel.stripWrap.repaint();
40937         }
40938         this.fireEvent("activate", this.tabPanel, this);
40939     },
40940
40941     /**
40942      * Returns true if this tab is the active tab.
40943      * @return {Boolean}
40944      */
40945     isActive : function(){
40946         return this.tabPanel.getActiveTab() == this;
40947     },
40948
40949     /**
40950      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40951      */
40952     hide : function(){
40953         this.status_node.removeClass("active");
40954         this.hideAction();
40955         this.fireEvent("deactivate", this.tabPanel, this);
40956     },
40957
40958     hideAction : function(){
40959         this.bodyEl.hide();
40960         this.bodyEl.setStyle("position", "absolute");
40961         this.bodyEl.setLeft("-20000px");
40962         this.bodyEl.setTop("-20000px");
40963     },
40964
40965     showAction : function(){
40966         this.bodyEl.setStyle("position", "relative");
40967         this.bodyEl.setTop("");
40968         this.bodyEl.setLeft("");
40969         this.bodyEl.show();
40970     },
40971
40972     /**
40973      * Set the tooltip for the tab.
40974      * @param {String} tooltip The tab's tooltip
40975      */
40976     setTooltip : function(text){
40977         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40978             this.textEl.dom.qtip = text;
40979             this.textEl.dom.removeAttribute('title');
40980         }else{
40981             this.textEl.dom.title = text;
40982         }
40983     },
40984
40985     onTabClick : function(e){
40986         e.preventDefault();
40987         this.tabPanel.activate(this.id);
40988     },
40989
40990     onTabMouseDown : function(e){
40991         e.preventDefault();
40992         this.tabPanel.activate(this.id);
40993     },
40994 /*
40995     getWidth : function(){
40996         return this.inner.getWidth();
40997     },
40998
40999     setWidth : function(width){
41000         var iwidth = width - this.linode.getPadding("lr");
41001         this.inner.setWidth(iwidth);
41002         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41003         this.linode.setWidth(width);
41004     },
41005 */
41006     /**
41007      * Show or hide the tab
41008      * @param {Boolean} hidden True to hide or false to show.
41009      */
41010     setHidden : function(hidden){
41011         this.hidden = hidden;
41012         this.linode.setStyle("display", hidden ? "none" : "");
41013     },
41014
41015     /**
41016      * Returns true if this tab is "hidden"
41017      * @return {Boolean}
41018      */
41019     isHidden : function(){
41020         return this.hidden;
41021     },
41022
41023     /**
41024      * Returns the text for this tab
41025      * @return {String}
41026      */
41027     getText : function(){
41028         return this.text;
41029     },
41030     /*
41031     autoSize : function(){
41032         //this.el.beginMeasure();
41033         this.textEl.setWidth(1);
41034         /*
41035          *  #2804 [new] Tabs in Roojs
41036          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41037          */
41038         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41039         //this.el.endMeasure();
41040     //},
41041
41042     /**
41043      * Sets the text for the tab (Note: this also sets the tooltip text)
41044      * @param {String} text The tab's text and tooltip
41045      */
41046     setText : function(text){
41047         this.text = text;
41048         this.textEl.update(text);
41049         this.setTooltip(text);
41050         //if(!this.tabPanel.resizeTabs){
41051         //    this.autoSize();
41052         //}
41053     },
41054     /**
41055      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41056      */
41057     activate : function(){
41058         this.tabPanel.activate(this.id);
41059     },
41060
41061     /**
41062      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41063      */
41064     disable : function(){
41065         if(this.tabPanel.active != this){
41066             this.disabled = true;
41067             this.status_node.addClass("disabled");
41068         }
41069     },
41070
41071     /**
41072      * Enables this TabPanelItem if it was previously disabled.
41073      */
41074     enable : function(){
41075         this.disabled = false;
41076         this.status_node.removeClass("disabled");
41077     },
41078
41079     /**
41080      * Sets the content for this TabPanelItem.
41081      * @param {String} content The content
41082      * @param {Boolean} loadScripts true to look for and load scripts
41083      */
41084     setContent : function(content, loadScripts){
41085         this.bodyEl.update(content, loadScripts);
41086     },
41087
41088     /**
41089      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41090      * @return {Roo.UpdateManager} The UpdateManager
41091      */
41092     getUpdateManager : function(){
41093         return this.bodyEl.getUpdateManager();
41094     },
41095
41096     /**
41097      * Set a URL to be used to load the content for this TabPanelItem.
41098      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41099      * @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)
41100      * @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)
41101      * @return {Roo.UpdateManager} The UpdateManager
41102      */
41103     setUrl : function(url, params, loadOnce){
41104         if(this.refreshDelegate){
41105             this.un('activate', this.refreshDelegate);
41106         }
41107         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41108         this.on("activate", this.refreshDelegate);
41109         return this.bodyEl.getUpdateManager();
41110     },
41111
41112     /** @private */
41113     _handleRefresh : function(url, params, loadOnce){
41114         if(!loadOnce || !this.loaded){
41115             var updater = this.bodyEl.getUpdateManager();
41116             updater.update(url, params, this._setLoaded.createDelegate(this));
41117         }
41118     },
41119
41120     /**
41121      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41122      *   Will fail silently if the setUrl method has not been called.
41123      *   This does not activate the panel, just updates its content.
41124      */
41125     refresh : function(){
41126         if(this.refreshDelegate){
41127            this.loaded = false;
41128            this.refreshDelegate();
41129         }
41130     },
41131
41132     /** @private */
41133     _setLoaded : function(){
41134         this.loaded = true;
41135     },
41136
41137     /** @private */
41138     closeClick : function(e){
41139         var o = {};
41140         e.stopEvent();
41141         this.fireEvent("beforeclose", this, o);
41142         if(o.cancel !== true){
41143             this.tabPanel.removeTab(this.id);
41144         }
41145     },
41146     /**
41147      * The text displayed in the tooltip for the close icon.
41148      * @type String
41149      */
41150     closeText : "Close this tab"
41151 });
41152 /**
41153 *    This script refer to:
41154 *    Title: International Telephone Input
41155 *    Author: Jack O'Connor
41156 *    Code version:  v12.1.12
41157 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41158 **/
41159
41160 Roo.bootstrap.PhoneInputData = function() {
41161     var d = [
41162       [
41163         "Afghanistan (‫افغانستان‬‎)",
41164         "af",
41165         "93"
41166       ],
41167       [
41168         "Albania (Shqipëri)",
41169         "al",
41170         "355"
41171       ],
41172       [
41173         "Algeria (‫الجزائر‬‎)",
41174         "dz",
41175         "213"
41176       ],
41177       [
41178         "American Samoa",
41179         "as",
41180         "1684"
41181       ],
41182       [
41183         "Andorra",
41184         "ad",
41185         "376"
41186       ],
41187       [
41188         "Angola",
41189         "ao",
41190         "244"
41191       ],
41192       [
41193         "Anguilla",
41194         "ai",
41195         "1264"
41196       ],
41197       [
41198         "Antigua and Barbuda",
41199         "ag",
41200         "1268"
41201       ],
41202       [
41203         "Argentina",
41204         "ar",
41205         "54"
41206       ],
41207       [
41208         "Armenia (Հայաստան)",
41209         "am",
41210         "374"
41211       ],
41212       [
41213         "Aruba",
41214         "aw",
41215         "297"
41216       ],
41217       [
41218         "Australia",
41219         "au",
41220         "61",
41221         0
41222       ],
41223       [
41224         "Austria (Österreich)",
41225         "at",
41226         "43"
41227       ],
41228       [
41229         "Azerbaijan (Azərbaycan)",
41230         "az",
41231         "994"
41232       ],
41233       [
41234         "Bahamas",
41235         "bs",
41236         "1242"
41237       ],
41238       [
41239         "Bahrain (‫البحرين‬‎)",
41240         "bh",
41241         "973"
41242       ],
41243       [
41244         "Bangladesh (বাংলাদেশ)",
41245         "bd",
41246         "880"
41247       ],
41248       [
41249         "Barbados",
41250         "bb",
41251         "1246"
41252       ],
41253       [
41254         "Belarus (Беларусь)",
41255         "by",
41256         "375"
41257       ],
41258       [
41259         "Belgium (België)",
41260         "be",
41261         "32"
41262       ],
41263       [
41264         "Belize",
41265         "bz",
41266         "501"
41267       ],
41268       [
41269         "Benin (Bénin)",
41270         "bj",
41271         "229"
41272       ],
41273       [
41274         "Bermuda",
41275         "bm",
41276         "1441"
41277       ],
41278       [
41279         "Bhutan (འབྲུག)",
41280         "bt",
41281         "975"
41282       ],
41283       [
41284         "Bolivia",
41285         "bo",
41286         "591"
41287       ],
41288       [
41289         "Bosnia and Herzegovina (Босна и Херцеговина)",
41290         "ba",
41291         "387"
41292       ],
41293       [
41294         "Botswana",
41295         "bw",
41296         "267"
41297       ],
41298       [
41299         "Brazil (Brasil)",
41300         "br",
41301         "55"
41302       ],
41303       [
41304         "British Indian Ocean Territory",
41305         "io",
41306         "246"
41307       ],
41308       [
41309         "British Virgin Islands",
41310         "vg",
41311         "1284"
41312       ],
41313       [
41314         "Brunei",
41315         "bn",
41316         "673"
41317       ],
41318       [
41319         "Bulgaria (България)",
41320         "bg",
41321         "359"
41322       ],
41323       [
41324         "Burkina Faso",
41325         "bf",
41326         "226"
41327       ],
41328       [
41329         "Burundi (Uburundi)",
41330         "bi",
41331         "257"
41332       ],
41333       [
41334         "Cambodia (កម្ពុជា)",
41335         "kh",
41336         "855"
41337       ],
41338       [
41339         "Cameroon (Cameroun)",
41340         "cm",
41341         "237"
41342       ],
41343       [
41344         "Canada",
41345         "ca",
41346         "1",
41347         1,
41348         ["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"]
41349       ],
41350       [
41351         "Cape Verde (Kabu Verdi)",
41352         "cv",
41353         "238"
41354       ],
41355       [
41356         "Caribbean Netherlands",
41357         "bq",
41358         "599",
41359         1
41360       ],
41361       [
41362         "Cayman Islands",
41363         "ky",
41364         "1345"
41365       ],
41366       [
41367         "Central African Republic (République centrafricaine)",
41368         "cf",
41369         "236"
41370       ],
41371       [
41372         "Chad (Tchad)",
41373         "td",
41374         "235"
41375       ],
41376       [
41377         "Chile",
41378         "cl",
41379         "56"
41380       ],
41381       [
41382         "China (中国)",
41383         "cn",
41384         "86"
41385       ],
41386       [
41387         "Christmas Island",
41388         "cx",
41389         "61",
41390         2
41391       ],
41392       [
41393         "Cocos (Keeling) Islands",
41394         "cc",
41395         "61",
41396         1
41397       ],
41398       [
41399         "Colombia",
41400         "co",
41401         "57"
41402       ],
41403       [
41404         "Comoros (‫جزر القمر‬‎)",
41405         "km",
41406         "269"
41407       ],
41408       [
41409         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41410         "cd",
41411         "243"
41412       ],
41413       [
41414         "Congo (Republic) (Congo-Brazzaville)",
41415         "cg",
41416         "242"
41417       ],
41418       [
41419         "Cook Islands",
41420         "ck",
41421         "682"
41422       ],
41423       [
41424         "Costa Rica",
41425         "cr",
41426         "506"
41427       ],
41428       [
41429         "Côte d’Ivoire",
41430         "ci",
41431         "225"
41432       ],
41433       [
41434         "Croatia (Hrvatska)",
41435         "hr",
41436         "385"
41437       ],
41438       [
41439         "Cuba",
41440         "cu",
41441         "53"
41442       ],
41443       [
41444         "Curaçao",
41445         "cw",
41446         "599",
41447         0
41448       ],
41449       [
41450         "Cyprus (Κύπρος)",
41451         "cy",
41452         "357"
41453       ],
41454       [
41455         "Czech Republic (Česká republika)",
41456         "cz",
41457         "420"
41458       ],
41459       [
41460         "Denmark (Danmark)",
41461         "dk",
41462         "45"
41463       ],
41464       [
41465         "Djibouti",
41466         "dj",
41467         "253"
41468       ],
41469       [
41470         "Dominica",
41471         "dm",
41472         "1767"
41473       ],
41474       [
41475         "Dominican Republic (República Dominicana)",
41476         "do",
41477         "1",
41478         2,
41479         ["809", "829", "849"]
41480       ],
41481       [
41482         "Ecuador",
41483         "ec",
41484         "593"
41485       ],
41486       [
41487         "Egypt (‫مصر‬‎)",
41488         "eg",
41489         "20"
41490       ],
41491       [
41492         "El Salvador",
41493         "sv",
41494         "503"
41495       ],
41496       [
41497         "Equatorial Guinea (Guinea Ecuatorial)",
41498         "gq",
41499         "240"
41500       ],
41501       [
41502         "Eritrea",
41503         "er",
41504         "291"
41505       ],
41506       [
41507         "Estonia (Eesti)",
41508         "ee",
41509         "372"
41510       ],
41511       [
41512         "Ethiopia",
41513         "et",
41514         "251"
41515       ],
41516       [
41517         "Falkland Islands (Islas Malvinas)",
41518         "fk",
41519         "500"
41520       ],
41521       [
41522         "Faroe Islands (Føroyar)",
41523         "fo",
41524         "298"
41525       ],
41526       [
41527         "Fiji",
41528         "fj",
41529         "679"
41530       ],
41531       [
41532         "Finland (Suomi)",
41533         "fi",
41534         "358",
41535         0
41536       ],
41537       [
41538         "France",
41539         "fr",
41540         "33"
41541       ],
41542       [
41543         "French Guiana (Guyane française)",
41544         "gf",
41545         "594"
41546       ],
41547       [
41548         "French Polynesia (Polynésie française)",
41549         "pf",
41550         "689"
41551       ],
41552       [
41553         "Gabon",
41554         "ga",
41555         "241"
41556       ],
41557       [
41558         "Gambia",
41559         "gm",
41560         "220"
41561       ],
41562       [
41563         "Georgia (საქართველო)",
41564         "ge",
41565         "995"
41566       ],
41567       [
41568         "Germany (Deutschland)",
41569         "de",
41570         "49"
41571       ],
41572       [
41573         "Ghana (Gaana)",
41574         "gh",
41575         "233"
41576       ],
41577       [
41578         "Gibraltar",
41579         "gi",
41580         "350"
41581       ],
41582       [
41583         "Greece (Ελλάδα)",
41584         "gr",
41585         "30"
41586       ],
41587       [
41588         "Greenland (Kalaallit Nunaat)",
41589         "gl",
41590         "299"
41591       ],
41592       [
41593         "Grenada",
41594         "gd",
41595         "1473"
41596       ],
41597       [
41598         "Guadeloupe",
41599         "gp",
41600         "590",
41601         0
41602       ],
41603       [
41604         "Guam",
41605         "gu",
41606         "1671"
41607       ],
41608       [
41609         "Guatemala",
41610         "gt",
41611         "502"
41612       ],
41613       [
41614         "Guernsey",
41615         "gg",
41616         "44",
41617         1
41618       ],
41619       [
41620         "Guinea (Guinée)",
41621         "gn",
41622         "224"
41623       ],
41624       [
41625         "Guinea-Bissau (Guiné Bissau)",
41626         "gw",
41627         "245"
41628       ],
41629       [
41630         "Guyana",
41631         "gy",
41632         "592"
41633       ],
41634       [
41635         "Haiti",
41636         "ht",
41637         "509"
41638       ],
41639       [
41640         "Honduras",
41641         "hn",
41642         "504"
41643       ],
41644       [
41645         "Hong Kong (香港)",
41646         "hk",
41647         "852"
41648       ],
41649       [
41650         "Hungary (Magyarország)",
41651         "hu",
41652         "36"
41653       ],
41654       [
41655         "Iceland (Ísland)",
41656         "is",
41657         "354"
41658       ],
41659       [
41660         "India (भारत)",
41661         "in",
41662         "91"
41663       ],
41664       [
41665         "Indonesia",
41666         "id",
41667         "62"
41668       ],
41669       [
41670         "Iran (‫ایران‬‎)",
41671         "ir",
41672         "98"
41673       ],
41674       [
41675         "Iraq (‫العراق‬‎)",
41676         "iq",
41677         "964"
41678       ],
41679       [
41680         "Ireland",
41681         "ie",
41682         "353"
41683       ],
41684       [
41685         "Isle of Man",
41686         "im",
41687         "44",
41688         2
41689       ],
41690       [
41691         "Israel (‫ישראל‬‎)",
41692         "il",
41693         "972"
41694       ],
41695       [
41696         "Italy (Italia)",
41697         "it",
41698         "39",
41699         0
41700       ],
41701       [
41702         "Jamaica",
41703         "jm",
41704         "1876"
41705       ],
41706       [
41707         "Japan (日本)",
41708         "jp",
41709         "81"
41710       ],
41711       [
41712         "Jersey",
41713         "je",
41714         "44",
41715         3
41716       ],
41717       [
41718         "Jordan (‫الأردن‬‎)",
41719         "jo",
41720         "962"
41721       ],
41722       [
41723         "Kazakhstan (Казахстан)",
41724         "kz",
41725         "7",
41726         1
41727       ],
41728       [
41729         "Kenya",
41730         "ke",
41731         "254"
41732       ],
41733       [
41734         "Kiribati",
41735         "ki",
41736         "686"
41737       ],
41738       [
41739         "Kosovo",
41740         "xk",
41741         "383"
41742       ],
41743       [
41744         "Kuwait (‫الكويت‬‎)",
41745         "kw",
41746         "965"
41747       ],
41748       [
41749         "Kyrgyzstan (Кыргызстан)",
41750         "kg",
41751         "996"
41752       ],
41753       [
41754         "Laos (ລາວ)",
41755         "la",
41756         "856"
41757       ],
41758       [
41759         "Latvia (Latvija)",
41760         "lv",
41761         "371"
41762       ],
41763       [
41764         "Lebanon (‫لبنان‬‎)",
41765         "lb",
41766         "961"
41767       ],
41768       [
41769         "Lesotho",
41770         "ls",
41771         "266"
41772       ],
41773       [
41774         "Liberia",
41775         "lr",
41776         "231"
41777       ],
41778       [
41779         "Libya (‫ليبيا‬‎)",
41780         "ly",
41781         "218"
41782       ],
41783       [
41784         "Liechtenstein",
41785         "li",
41786         "423"
41787       ],
41788       [
41789         "Lithuania (Lietuva)",
41790         "lt",
41791         "370"
41792       ],
41793       [
41794         "Luxembourg",
41795         "lu",
41796         "352"
41797       ],
41798       [
41799         "Macau (澳門)",
41800         "mo",
41801         "853"
41802       ],
41803       [
41804         "Macedonia (FYROM) (Македонија)",
41805         "mk",
41806         "389"
41807       ],
41808       [
41809         "Madagascar (Madagasikara)",
41810         "mg",
41811         "261"
41812       ],
41813       [
41814         "Malawi",
41815         "mw",
41816         "265"
41817       ],
41818       [
41819         "Malaysia",
41820         "my",
41821         "60"
41822       ],
41823       [
41824         "Maldives",
41825         "mv",
41826         "960"
41827       ],
41828       [
41829         "Mali",
41830         "ml",
41831         "223"
41832       ],
41833       [
41834         "Malta",
41835         "mt",
41836         "356"
41837       ],
41838       [
41839         "Marshall Islands",
41840         "mh",
41841         "692"
41842       ],
41843       [
41844         "Martinique",
41845         "mq",
41846         "596"
41847       ],
41848       [
41849         "Mauritania (‫موريتانيا‬‎)",
41850         "mr",
41851         "222"
41852       ],
41853       [
41854         "Mauritius (Moris)",
41855         "mu",
41856         "230"
41857       ],
41858       [
41859         "Mayotte",
41860         "yt",
41861         "262",
41862         1
41863       ],
41864       [
41865         "Mexico (México)",
41866         "mx",
41867         "52"
41868       ],
41869       [
41870         "Micronesia",
41871         "fm",
41872         "691"
41873       ],
41874       [
41875         "Moldova (Republica Moldova)",
41876         "md",
41877         "373"
41878       ],
41879       [
41880         "Monaco",
41881         "mc",
41882         "377"
41883       ],
41884       [
41885         "Mongolia (Монгол)",
41886         "mn",
41887         "976"
41888       ],
41889       [
41890         "Montenegro (Crna Gora)",
41891         "me",
41892         "382"
41893       ],
41894       [
41895         "Montserrat",
41896         "ms",
41897         "1664"
41898       ],
41899       [
41900         "Morocco (‫المغرب‬‎)",
41901         "ma",
41902         "212",
41903         0
41904       ],
41905       [
41906         "Mozambique (Moçambique)",
41907         "mz",
41908         "258"
41909       ],
41910       [
41911         "Myanmar (Burma) (မြန်မာ)",
41912         "mm",
41913         "95"
41914       ],
41915       [
41916         "Namibia (Namibië)",
41917         "na",
41918         "264"
41919       ],
41920       [
41921         "Nauru",
41922         "nr",
41923         "674"
41924       ],
41925       [
41926         "Nepal (नेपाल)",
41927         "np",
41928         "977"
41929       ],
41930       [
41931         "Netherlands (Nederland)",
41932         "nl",
41933         "31"
41934       ],
41935       [
41936         "New Caledonia (Nouvelle-Calédonie)",
41937         "nc",
41938         "687"
41939       ],
41940       [
41941         "New Zealand",
41942         "nz",
41943         "64"
41944       ],
41945       [
41946         "Nicaragua",
41947         "ni",
41948         "505"
41949       ],
41950       [
41951         "Niger (Nijar)",
41952         "ne",
41953         "227"
41954       ],
41955       [
41956         "Nigeria",
41957         "ng",
41958         "234"
41959       ],
41960       [
41961         "Niue",
41962         "nu",
41963         "683"
41964       ],
41965       [
41966         "Norfolk Island",
41967         "nf",
41968         "672"
41969       ],
41970       [
41971         "North Korea (조선 민주주의 인민 공화국)",
41972         "kp",
41973         "850"
41974       ],
41975       [
41976         "Northern Mariana Islands",
41977         "mp",
41978         "1670"
41979       ],
41980       [
41981         "Norway (Norge)",
41982         "no",
41983         "47",
41984         0
41985       ],
41986       [
41987         "Oman (‫عُمان‬‎)",
41988         "om",
41989         "968"
41990       ],
41991       [
41992         "Pakistan (‫پاکستان‬‎)",
41993         "pk",
41994         "92"
41995       ],
41996       [
41997         "Palau",
41998         "pw",
41999         "680"
42000       ],
42001       [
42002         "Palestine (‫فلسطين‬‎)",
42003         "ps",
42004         "970"
42005       ],
42006       [
42007         "Panama (Panamá)",
42008         "pa",
42009         "507"
42010       ],
42011       [
42012         "Papua New Guinea",
42013         "pg",
42014         "675"
42015       ],
42016       [
42017         "Paraguay",
42018         "py",
42019         "595"
42020       ],
42021       [
42022         "Peru (Perú)",
42023         "pe",
42024         "51"
42025       ],
42026       [
42027         "Philippines",
42028         "ph",
42029         "63"
42030       ],
42031       [
42032         "Poland (Polska)",
42033         "pl",
42034         "48"
42035       ],
42036       [
42037         "Portugal",
42038         "pt",
42039         "351"
42040       ],
42041       [
42042         "Puerto Rico",
42043         "pr",
42044         "1",
42045         3,
42046         ["787", "939"]
42047       ],
42048       [
42049         "Qatar (‫قطر‬‎)",
42050         "qa",
42051         "974"
42052       ],
42053       [
42054         "Réunion (La Réunion)",
42055         "re",
42056         "262",
42057         0
42058       ],
42059       [
42060         "Romania (România)",
42061         "ro",
42062         "40"
42063       ],
42064       [
42065         "Russia (Россия)",
42066         "ru",
42067         "7",
42068         0
42069       ],
42070       [
42071         "Rwanda",
42072         "rw",
42073         "250"
42074       ],
42075       [
42076         "Saint Barthélemy",
42077         "bl",
42078         "590",
42079         1
42080       ],
42081       [
42082         "Saint Helena",
42083         "sh",
42084         "290"
42085       ],
42086       [
42087         "Saint Kitts and Nevis",
42088         "kn",
42089         "1869"
42090       ],
42091       [
42092         "Saint Lucia",
42093         "lc",
42094         "1758"
42095       ],
42096       [
42097         "Saint Martin (Saint-Martin (partie française))",
42098         "mf",
42099         "590",
42100         2
42101       ],
42102       [
42103         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42104         "pm",
42105         "508"
42106       ],
42107       [
42108         "Saint Vincent and the Grenadines",
42109         "vc",
42110         "1784"
42111       ],
42112       [
42113         "Samoa",
42114         "ws",
42115         "685"
42116       ],
42117       [
42118         "San Marino",
42119         "sm",
42120         "378"
42121       ],
42122       [
42123         "São Tomé and Príncipe (São Tomé e Príncipe)",
42124         "st",
42125         "239"
42126       ],
42127       [
42128         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42129         "sa",
42130         "966"
42131       ],
42132       [
42133         "Senegal (Sénégal)",
42134         "sn",
42135         "221"
42136       ],
42137       [
42138         "Serbia (Србија)",
42139         "rs",
42140         "381"
42141       ],
42142       [
42143         "Seychelles",
42144         "sc",
42145         "248"
42146       ],
42147       [
42148         "Sierra Leone",
42149         "sl",
42150         "232"
42151       ],
42152       [
42153         "Singapore",
42154         "sg",
42155         "65"
42156       ],
42157       [
42158         "Sint Maarten",
42159         "sx",
42160         "1721"
42161       ],
42162       [
42163         "Slovakia (Slovensko)",
42164         "sk",
42165         "421"
42166       ],
42167       [
42168         "Slovenia (Slovenija)",
42169         "si",
42170         "386"
42171       ],
42172       [
42173         "Solomon Islands",
42174         "sb",
42175         "677"
42176       ],
42177       [
42178         "Somalia (Soomaaliya)",
42179         "so",
42180         "252"
42181       ],
42182       [
42183         "South Africa",
42184         "za",
42185         "27"
42186       ],
42187       [
42188         "South Korea (대한민국)",
42189         "kr",
42190         "82"
42191       ],
42192       [
42193         "South Sudan (‫جنوب السودان‬‎)",
42194         "ss",
42195         "211"
42196       ],
42197       [
42198         "Spain (España)",
42199         "es",
42200         "34"
42201       ],
42202       [
42203         "Sri Lanka (ශ්‍රී ලංකාව)",
42204         "lk",
42205         "94"
42206       ],
42207       [
42208         "Sudan (‫السودان‬‎)",
42209         "sd",
42210         "249"
42211       ],
42212       [
42213         "Suriname",
42214         "sr",
42215         "597"
42216       ],
42217       [
42218         "Svalbard and Jan Mayen",
42219         "sj",
42220         "47",
42221         1
42222       ],
42223       [
42224         "Swaziland",
42225         "sz",
42226         "268"
42227       ],
42228       [
42229         "Sweden (Sverige)",
42230         "se",
42231         "46"
42232       ],
42233       [
42234         "Switzerland (Schweiz)",
42235         "ch",
42236         "41"
42237       ],
42238       [
42239         "Syria (‫سوريا‬‎)",
42240         "sy",
42241         "963"
42242       ],
42243       [
42244         "Taiwan (台灣)",
42245         "tw",
42246         "886"
42247       ],
42248       [
42249         "Tajikistan",
42250         "tj",
42251         "992"
42252       ],
42253       [
42254         "Tanzania",
42255         "tz",
42256         "255"
42257       ],
42258       [
42259         "Thailand (ไทย)",
42260         "th",
42261         "66"
42262       ],
42263       [
42264         "Timor-Leste",
42265         "tl",
42266         "670"
42267       ],
42268       [
42269         "Togo",
42270         "tg",
42271         "228"
42272       ],
42273       [
42274         "Tokelau",
42275         "tk",
42276         "690"
42277       ],
42278       [
42279         "Tonga",
42280         "to",
42281         "676"
42282       ],
42283       [
42284         "Trinidad and Tobago",
42285         "tt",
42286         "1868"
42287       ],
42288       [
42289         "Tunisia (‫تونس‬‎)",
42290         "tn",
42291         "216"
42292       ],
42293       [
42294         "Turkey (Türkiye)",
42295         "tr",
42296         "90"
42297       ],
42298       [
42299         "Turkmenistan",
42300         "tm",
42301         "993"
42302       ],
42303       [
42304         "Turks and Caicos Islands",
42305         "tc",
42306         "1649"
42307       ],
42308       [
42309         "Tuvalu",
42310         "tv",
42311         "688"
42312       ],
42313       [
42314         "U.S. Virgin Islands",
42315         "vi",
42316         "1340"
42317       ],
42318       [
42319         "Uganda",
42320         "ug",
42321         "256"
42322       ],
42323       [
42324         "Ukraine (Україна)",
42325         "ua",
42326         "380"
42327       ],
42328       [
42329         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42330         "ae",
42331         "971"
42332       ],
42333       [
42334         "United Kingdom",
42335         "gb",
42336         "44",
42337         0
42338       ],
42339       [
42340         "United States",
42341         "us",
42342         "1",
42343         0
42344       ],
42345       [
42346         "Uruguay",
42347         "uy",
42348         "598"
42349       ],
42350       [
42351         "Uzbekistan (Oʻzbekiston)",
42352         "uz",
42353         "998"
42354       ],
42355       [
42356         "Vanuatu",
42357         "vu",
42358         "678"
42359       ],
42360       [
42361         "Vatican City (Città del Vaticano)",
42362         "va",
42363         "39",
42364         1
42365       ],
42366       [
42367         "Venezuela",
42368         "ve",
42369         "58"
42370       ],
42371       [
42372         "Vietnam (Việt Nam)",
42373         "vn",
42374         "84"
42375       ],
42376       [
42377         "Wallis and Futuna (Wallis-et-Futuna)",
42378         "wf",
42379         "681"
42380       ],
42381       [
42382         "Western Sahara (‫الصحراء الغربية‬‎)",
42383         "eh",
42384         "212",
42385         1
42386       ],
42387       [
42388         "Yemen (‫اليمن‬‎)",
42389         "ye",
42390         "967"
42391       ],
42392       [
42393         "Zambia",
42394         "zm",
42395         "260"
42396       ],
42397       [
42398         "Zimbabwe",
42399         "zw",
42400         "263"
42401       ],
42402       [
42403         "Åland Islands",
42404         "ax",
42405         "358",
42406         1
42407       ]
42408   ];
42409   
42410   return d;
42411 }/**
42412 *    This script refer to:
42413 *    Title: International Telephone Input
42414 *    Author: Jack O'Connor
42415 *    Code version:  v12.1.12
42416 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42417 **/
42418
42419 /**
42420  * @class Roo.bootstrap.PhoneInput
42421  * @extends Roo.bootstrap.TriggerField
42422  * An input with International dial-code selection
42423  
42424  * @cfg {String} defaultDialCode default '+852'
42425  * @cfg {Array} preferedCountries default []
42426   
42427  * @constructor
42428  * Create a new PhoneInput.
42429  * @param {Object} config Configuration options
42430  */
42431
42432 Roo.bootstrap.PhoneInput = function(config) {
42433     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42434 };
42435
42436 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42437         
42438         listWidth: undefined,
42439         
42440         selectedClass: 'active',
42441         
42442         invalidClass : "has-warning",
42443         
42444         validClass: 'has-success',
42445         
42446         allowed: '0123456789',
42447         
42448         max_length: 15,
42449         
42450         /**
42451          * @cfg {String} defaultDialCode The default dial code when initializing the input
42452          */
42453         defaultDialCode: '+852',
42454         
42455         /**
42456          * @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
42457          */
42458         preferedCountries: false,
42459         
42460         getAutoCreate : function()
42461         {
42462             var data = Roo.bootstrap.PhoneInputData();
42463             var align = this.labelAlign || this.parentLabelAlign();
42464             var id = Roo.id();
42465             
42466             this.allCountries = [];
42467             this.dialCodeMapping = [];
42468             
42469             for (var i = 0; i < data.length; i++) {
42470               var c = data[i];
42471               this.allCountries[i] = {
42472                 name: c[0],
42473                 iso2: c[1],
42474                 dialCode: c[2],
42475                 priority: c[3] || 0,
42476                 areaCodes: c[4] || null
42477               };
42478               this.dialCodeMapping[c[2]] = {
42479                   name: c[0],
42480                   iso2: c[1],
42481                   priority: c[3] || 0,
42482                   areaCodes: c[4] || null
42483               };
42484             }
42485             
42486             var cfg = {
42487                 cls: 'form-group',
42488                 cn: []
42489             };
42490             
42491             var input =  {
42492                 tag: 'input',
42493                 id : id,
42494                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42495                 maxlength: this.max_length,
42496                 cls : 'form-control tel-input',
42497                 autocomplete: 'new-password'
42498             };
42499             
42500             var hiddenInput = {
42501                 tag: 'input',
42502                 type: 'hidden',
42503                 cls: 'hidden-tel-input'
42504             };
42505             
42506             if (this.name) {
42507                 hiddenInput.name = this.name;
42508             }
42509             
42510             if (this.disabled) {
42511                 input.disabled = true;
42512             }
42513             
42514             var flag_container = {
42515                 tag: 'div',
42516                 cls: 'flag-box',
42517                 cn: [
42518                     {
42519                         tag: 'div',
42520                         cls: 'flag'
42521                     },
42522                     {
42523                         tag: 'div',
42524                         cls: 'caret'
42525                     }
42526                 ]
42527             };
42528             
42529             var box = {
42530                 tag: 'div',
42531                 cls: this.hasFeedback ? 'has-feedback' : '',
42532                 cn: [
42533                     hiddenInput,
42534                     input,
42535                     {
42536                         tag: 'input',
42537                         cls: 'dial-code-holder',
42538                         disabled: true
42539                     }
42540                 ]
42541             };
42542             
42543             var container = {
42544                 cls: 'roo-select2-container input-group',
42545                 cn: [
42546                     flag_container,
42547                     box
42548                 ]
42549             };
42550             
42551             if (this.fieldLabel.length) {
42552                 var indicator = {
42553                     tag: 'i',
42554                     tooltip: 'This field is required'
42555                 };
42556                 
42557                 var label = {
42558                     tag: 'label',
42559                     'for':  id,
42560                     cls: 'control-label',
42561                     cn: []
42562                 };
42563                 
42564                 var label_text = {
42565                     tag: 'span',
42566                     html: this.fieldLabel
42567                 };
42568                 
42569                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42570                 label.cn = [
42571                     indicator,
42572                     label_text
42573                 ];
42574                 
42575                 if(this.indicatorpos == 'right') {
42576                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42577                     label.cn = [
42578                         label_text,
42579                         indicator
42580                     ];
42581                 }
42582                 
42583                 if(align == 'left') {
42584                     container = {
42585                         tag: 'div',
42586                         cn: [
42587                             container
42588                         ]
42589                     };
42590                     
42591                     if(this.labelWidth > 12){
42592                         label.style = "width: " + this.labelWidth + 'px';
42593                     }
42594                     if(this.labelWidth < 13 && this.labelmd == 0){
42595                         this.labelmd = this.labelWidth;
42596                     }
42597                     if(this.labellg > 0){
42598                         label.cls += ' col-lg-' + this.labellg;
42599                         input.cls += ' col-lg-' + (12 - this.labellg);
42600                     }
42601                     if(this.labelmd > 0){
42602                         label.cls += ' col-md-' + this.labelmd;
42603                         container.cls += ' col-md-' + (12 - this.labelmd);
42604                     }
42605                     if(this.labelsm > 0){
42606                         label.cls += ' col-sm-' + this.labelsm;
42607                         container.cls += ' col-sm-' + (12 - this.labelsm);
42608                     }
42609                     if(this.labelxs > 0){
42610                         label.cls += ' col-xs-' + this.labelxs;
42611                         container.cls += ' col-xs-' + (12 - this.labelxs);
42612                     }
42613                 }
42614             }
42615             
42616             cfg.cn = [
42617                 label,
42618                 container
42619             ];
42620             
42621             var settings = this;
42622             
42623             ['xs','sm','md','lg'].map(function(size){
42624                 if (settings[size]) {
42625                     cfg.cls += ' col-' + size + '-' + settings[size];
42626                 }
42627             });
42628             
42629             this.store = new Roo.data.Store({
42630                 proxy : new Roo.data.MemoryProxy({}),
42631                 reader : new Roo.data.JsonReader({
42632                     fields : [
42633                         {
42634                             'name' : 'name',
42635                             'type' : 'string'
42636                         },
42637                         {
42638                             'name' : 'iso2',
42639                             'type' : 'string'
42640                         },
42641                         {
42642                             'name' : 'dialCode',
42643                             'type' : 'string'
42644                         },
42645                         {
42646                             'name' : 'priority',
42647                             'type' : 'string'
42648                         },
42649                         {
42650                             'name' : 'areaCodes',
42651                             'type' : 'string'
42652                         }
42653                     ]
42654                 })
42655             });
42656             
42657             if(!this.preferedCountries) {
42658                 this.preferedCountries = [
42659                     'hk',
42660                     'gb',
42661                     'us'
42662                 ];
42663             }
42664             
42665             var p = this.preferedCountries.reverse();
42666             
42667             if(p) {
42668                 for (var i = 0; i < p.length; i++) {
42669                     for (var j = 0; j < this.allCountries.length; j++) {
42670                         if(this.allCountries[j].iso2 == p[i]) {
42671                             var t = this.allCountries[j];
42672                             this.allCountries.splice(j,1);
42673                             this.allCountries.unshift(t);
42674                         }
42675                     } 
42676                 }
42677             }
42678             
42679             this.store.proxy.data = {
42680                 success: true,
42681                 data: this.allCountries
42682             };
42683             
42684             return cfg;
42685         },
42686         
42687         initEvents : function()
42688         {
42689             this.createList();
42690             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42691             
42692             this.indicator = this.indicatorEl();
42693             this.flag = this.flagEl();
42694             this.dialCodeHolder = this.dialCodeHolderEl();
42695             
42696             this.trigger = this.el.select('div.flag-box',true).first();
42697             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42698             
42699             var _this = this;
42700             
42701             (function(){
42702                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42703                 _this.list.setWidth(lw);
42704             }).defer(100);
42705             
42706             this.list.on('mouseover', this.onViewOver, this);
42707             this.list.on('mousemove', this.onViewMove, this);
42708             this.inputEl().on("keyup", this.onKeyUp, this);
42709             this.inputEl().on("keypress", this.onKeyPress, this);
42710             
42711             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42712
42713             this.view = new Roo.View(this.list, this.tpl, {
42714                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42715             });
42716             
42717             this.view.on('click', this.onViewClick, this);
42718             this.setValue(this.defaultDialCode);
42719         },
42720         
42721         onTriggerClick : function(e)
42722         {
42723             Roo.log('trigger click');
42724             if(this.disabled){
42725                 return;
42726             }
42727             
42728             if(this.isExpanded()){
42729                 this.collapse();
42730                 this.hasFocus = false;
42731             }else {
42732                 this.store.load({});
42733                 this.hasFocus = true;
42734                 this.expand();
42735             }
42736         },
42737         
42738         isExpanded : function()
42739         {
42740             return this.list.isVisible();
42741         },
42742         
42743         collapse : function()
42744         {
42745             if(!this.isExpanded()){
42746                 return;
42747             }
42748             this.list.hide();
42749             Roo.get(document).un('mousedown', this.collapseIf, this);
42750             Roo.get(document).un('mousewheel', this.collapseIf, this);
42751             this.fireEvent('collapse', this);
42752             this.validate();
42753         },
42754         
42755         expand : function()
42756         {
42757             Roo.log('expand');
42758
42759             if(this.isExpanded() || !this.hasFocus){
42760                 return;
42761             }
42762             
42763             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42764             this.list.setWidth(lw);
42765             
42766             this.list.show();
42767             this.restrictHeight();
42768             
42769             Roo.get(document).on('mousedown', this.collapseIf, this);
42770             Roo.get(document).on('mousewheel', this.collapseIf, this);
42771             
42772             this.fireEvent('expand', this);
42773         },
42774         
42775         restrictHeight : function()
42776         {
42777             this.list.alignTo(this.inputEl(), this.listAlign);
42778             this.list.alignTo(this.inputEl(), this.listAlign);
42779         },
42780         
42781         onViewOver : function(e, t)
42782         {
42783             if(this.inKeyMode){
42784                 return;
42785             }
42786             var item = this.view.findItemFromChild(t);
42787             
42788             if(item){
42789                 var index = this.view.indexOf(item);
42790                 this.select(index, false);
42791             }
42792         },
42793
42794         // private
42795         onViewClick : function(view, doFocus, el, e)
42796         {
42797             var index = this.view.getSelectedIndexes()[0];
42798             
42799             var r = this.store.getAt(index);
42800             
42801             if(r){
42802                 this.onSelect(r, index);
42803             }
42804             if(doFocus !== false && !this.blockFocus){
42805                 this.inputEl().focus();
42806             }
42807         },
42808         
42809         onViewMove : function(e, t)
42810         {
42811             this.inKeyMode = false;
42812         },
42813         
42814         select : function(index, scrollIntoView)
42815         {
42816             this.selectedIndex = index;
42817             this.view.select(index);
42818             if(scrollIntoView !== false){
42819                 var el = this.view.getNode(index);
42820                 if(el){
42821                     this.list.scrollChildIntoView(el, false);
42822                 }
42823             }
42824         },
42825         
42826         createList : function()
42827         {
42828             this.list = Roo.get(document.body).createChild({
42829                 tag: 'ul',
42830                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42831                 style: 'display:none'
42832             });
42833             
42834             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42835         },
42836         
42837         collapseIf : function(e)
42838         {
42839             var in_combo  = e.within(this.el);
42840             var in_list =  e.within(this.list);
42841             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42842             
42843             if (in_combo || in_list || is_list) {
42844                 return;
42845             }
42846             this.collapse();
42847         },
42848         
42849         onSelect : function(record, index)
42850         {
42851             if(this.fireEvent('beforeselect', this, record, index) !== false){
42852                 
42853                 this.setFlagClass(record.data.iso2);
42854                 this.setDialCode(record.data.dialCode);
42855                 this.hasFocus = false;
42856                 this.collapse();
42857                 this.fireEvent('select', this, record, index);
42858             }
42859         },
42860         
42861         flagEl : function()
42862         {
42863             var flag = this.el.select('div.flag',true).first();
42864             if(!flag){
42865                 return false;
42866             }
42867             return flag;
42868         },
42869         
42870         dialCodeHolderEl : function()
42871         {
42872             var d = this.el.select('input.dial-code-holder',true).first();
42873             if(!d){
42874                 return false;
42875             }
42876             return d;
42877         },
42878         
42879         setDialCode : function(v)
42880         {
42881             this.dialCodeHolder.dom.value = '+'+v;
42882         },
42883         
42884         setFlagClass : function(n)
42885         {
42886             this.flag.dom.className = 'flag '+n;
42887         },
42888         
42889         getValue : function()
42890         {
42891             var v = this.inputEl().getValue();
42892             if(this.dialCodeHolder) {
42893                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42894             }
42895             return v;
42896         },
42897         
42898         setValue : function(v)
42899         {
42900             var d = this.getDialCode(v);
42901             
42902             //invalid dial code
42903             if(v.length == 0 || !d || d.length == 0) {
42904                 if(this.rendered){
42905                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42906                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42907                 }
42908                 return;
42909             }
42910             
42911             //valid dial code
42912             this.setFlagClass(this.dialCodeMapping[d].iso2);
42913             this.setDialCode(d);
42914             this.inputEl().dom.value = v.replace('+'+d,'');
42915             this.hiddenEl().dom.value = this.getValue();
42916             
42917             this.validate();
42918         },
42919         
42920         getDialCode : function(v)
42921         {
42922             v = v ||  '';
42923             
42924             if (v.length == 0) {
42925                 return this.dialCodeHolder.dom.value;
42926             }
42927             
42928             var dialCode = "";
42929             if (v.charAt(0) != "+") {
42930                 return false;
42931             }
42932             var numericChars = "";
42933             for (var i = 1; i < v.length; i++) {
42934               var c = v.charAt(i);
42935               if (!isNaN(c)) {
42936                 numericChars += c;
42937                 if (this.dialCodeMapping[numericChars]) {
42938                   dialCode = v.substr(1, i);
42939                 }
42940                 if (numericChars.length == 4) {
42941                   break;
42942                 }
42943               }
42944             }
42945             return dialCode;
42946         },
42947         
42948         reset : function()
42949         {
42950             this.setValue(this.defaultDialCode);
42951             this.validate();
42952         },
42953         
42954         hiddenEl : function()
42955         {
42956             return this.el.select('input.hidden-tel-input',true).first();
42957         },
42958         
42959         // after setting val
42960         onKeyUp : function(e){
42961             this.setValue(this.getValue());
42962         },
42963         
42964         onKeyPress : function(e){
42965             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42966                 e.stopEvent();
42967             }
42968         }
42969         
42970 });
42971 /**
42972  * @class Roo.bootstrap.MoneyField
42973  * @extends Roo.bootstrap.ComboBox
42974  * Bootstrap MoneyField class
42975  * 
42976  * @constructor
42977  * Create a new MoneyField.
42978  * @param {Object} config Configuration options
42979  */
42980
42981 Roo.bootstrap.MoneyField = function(config) {
42982     
42983     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42984     
42985 };
42986
42987 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42988     
42989     /**
42990      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
42991      */
42992     allowDecimals : true,
42993     /**
42994      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
42995      */
42996     decimalSeparator : ".",
42997     /**
42998      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
42999      */
43000     decimalPrecision : 0,
43001     /**
43002      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43003      */
43004     allowNegative : true,
43005     /**
43006      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43007      */
43008     allowZero: true,
43009     /**
43010      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43011      */
43012     minValue : Number.NEGATIVE_INFINITY,
43013     /**
43014      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43015      */
43016     maxValue : Number.MAX_VALUE,
43017     /**
43018      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43019      */
43020     minText : "The minimum value for this field is {0}",
43021     /**
43022      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43023      */
43024     maxText : "The maximum value for this field is {0}",
43025     /**
43026      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43027      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43028      */
43029     nanText : "{0} is not a valid number",
43030     /**
43031      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43032      */
43033     castInt : true,
43034     /**
43035      * @cfg {String} defaults currency of the MoneyField
43036      * value should be in lkey
43037      */
43038     defaultCurrency : false,
43039     /**
43040      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43041      */
43042     thousandsDelimiter : false,
43043     /**
43044      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43045      */
43046     max_length: false,
43047     
43048     inputlg : 9,
43049     inputmd : 9,
43050     inputsm : 9,
43051     inputxs : 6,
43052     
43053     store : false,
43054     
43055     getAutoCreate : function()
43056     {
43057         var align = this.labelAlign || this.parentLabelAlign();
43058         
43059         var id = Roo.id();
43060
43061         var cfg = {
43062             cls: 'form-group',
43063             cn: []
43064         };
43065
43066         var input =  {
43067             tag: 'input',
43068             id : id,
43069             cls : 'form-control roo-money-amount-input',
43070             autocomplete: 'new-password'
43071         };
43072         
43073         var hiddenInput = {
43074             tag: 'input',
43075             type: 'hidden',
43076             id: Roo.id(),
43077             cls: 'hidden-number-input'
43078         };
43079         
43080         if(this.max_length) {
43081             input.maxlength = this.max_length; 
43082         }
43083         
43084         if (this.name) {
43085             hiddenInput.name = this.name;
43086         }
43087
43088         if (this.disabled) {
43089             input.disabled = true;
43090         }
43091
43092         var clg = 12 - this.inputlg;
43093         var cmd = 12 - this.inputmd;
43094         var csm = 12 - this.inputsm;
43095         var cxs = 12 - this.inputxs;
43096         
43097         var container = {
43098             tag : 'div',
43099             cls : 'row roo-money-field',
43100             cn : [
43101                 {
43102                     tag : 'div',
43103                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43104                     cn : [
43105                         {
43106                             tag : 'div',
43107                             cls: 'roo-select2-container input-group',
43108                             cn: [
43109                                 {
43110                                     tag : 'input',
43111                                     cls : 'form-control roo-money-currency-input',
43112                                     autocomplete: 'new-password',
43113                                     readOnly : 1,
43114                                     name : this.currencyName
43115                                 },
43116                                 {
43117                                     tag :'span',
43118                                     cls : 'input-group-addon',
43119                                     cn : [
43120                                         {
43121                                             tag: 'span',
43122                                             cls: 'caret'
43123                                         }
43124                                     ]
43125                                 }
43126                             ]
43127                         }
43128                     ]
43129                 },
43130                 {
43131                     tag : 'div',
43132                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43133                     cn : [
43134                         {
43135                             tag: 'div',
43136                             cls: this.hasFeedback ? 'has-feedback' : '',
43137                             cn: [
43138                                 input
43139                             ]
43140                         }
43141                     ]
43142                 }
43143             ]
43144             
43145         };
43146         
43147         if (this.fieldLabel.length) {
43148             var indicator = {
43149                 tag: 'i',
43150                 tooltip: 'This field is required'
43151             };
43152
43153             var label = {
43154                 tag: 'label',
43155                 'for':  id,
43156                 cls: 'control-label',
43157                 cn: []
43158             };
43159
43160             var label_text = {
43161                 tag: 'span',
43162                 html: this.fieldLabel
43163             };
43164
43165             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43166             label.cn = [
43167                 indicator,
43168                 label_text
43169             ];
43170
43171             if(this.indicatorpos == 'right') {
43172                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43173                 label.cn = [
43174                     label_text,
43175                     indicator
43176                 ];
43177             }
43178
43179             if(align == 'left') {
43180                 container = {
43181                     tag: 'div',
43182                     cn: [
43183                         container
43184                     ]
43185                 };
43186
43187                 if(this.labelWidth > 12){
43188                     label.style = "width: " + this.labelWidth + 'px';
43189                 }
43190                 if(this.labelWidth < 13 && this.labelmd == 0){
43191                     this.labelmd = this.labelWidth;
43192                 }
43193                 if(this.labellg > 0){
43194                     label.cls += ' col-lg-' + this.labellg;
43195                     input.cls += ' col-lg-' + (12 - this.labellg);
43196                 }
43197                 if(this.labelmd > 0){
43198                     label.cls += ' col-md-' + this.labelmd;
43199                     container.cls += ' col-md-' + (12 - this.labelmd);
43200                 }
43201                 if(this.labelsm > 0){
43202                     label.cls += ' col-sm-' + this.labelsm;
43203                     container.cls += ' col-sm-' + (12 - this.labelsm);
43204                 }
43205                 if(this.labelxs > 0){
43206                     label.cls += ' col-xs-' + this.labelxs;
43207                     container.cls += ' col-xs-' + (12 - this.labelxs);
43208                 }
43209             }
43210         }
43211
43212         cfg.cn = [
43213             label,
43214             container,
43215             hiddenInput
43216         ];
43217         
43218         var settings = this;
43219
43220         ['xs','sm','md','lg'].map(function(size){
43221             if (settings[size]) {
43222                 cfg.cls += ' col-' + size + '-' + settings[size];
43223             }
43224         });
43225         
43226         return cfg;
43227     },
43228     
43229     initEvents : function()
43230     {
43231         this.indicator = this.indicatorEl();
43232         
43233         this.initCurrencyEvent();
43234         
43235         this.initNumberEvent();
43236     },
43237     
43238     initCurrencyEvent : function()
43239     {
43240         if (!this.store) {
43241             throw "can not find store for combo";
43242         }
43243         
43244         this.store = Roo.factory(this.store, Roo.data);
43245         this.store.parent = this;
43246         
43247         this.createList();
43248         
43249         this.triggerEl = this.el.select('.input-group-addon', true).first();
43250         
43251         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43252         
43253         var _this = this;
43254         
43255         (function(){
43256             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43257             _this.list.setWidth(lw);
43258         }).defer(100);
43259         
43260         this.list.on('mouseover', this.onViewOver, this);
43261         this.list.on('mousemove', this.onViewMove, this);
43262         this.list.on('scroll', this.onViewScroll, this);
43263         
43264         if(!this.tpl){
43265             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43266         }
43267         
43268         this.view = new Roo.View(this.list, this.tpl, {
43269             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43270         });
43271         
43272         this.view.on('click', this.onViewClick, this);
43273         
43274         this.store.on('beforeload', this.onBeforeLoad, this);
43275         this.store.on('load', this.onLoad, this);
43276         this.store.on('loadexception', this.onLoadException, this);
43277         
43278         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43279             "up" : function(e){
43280                 this.inKeyMode = true;
43281                 this.selectPrev();
43282             },
43283
43284             "down" : function(e){
43285                 if(!this.isExpanded()){
43286                     this.onTriggerClick();
43287                 }else{
43288                     this.inKeyMode = true;
43289                     this.selectNext();
43290                 }
43291             },
43292
43293             "enter" : function(e){
43294                 this.collapse();
43295                 
43296                 if(this.fireEvent("specialkey", this, e)){
43297                     this.onViewClick(false);
43298                 }
43299                 
43300                 return true;
43301             },
43302
43303             "esc" : function(e){
43304                 this.collapse();
43305             },
43306
43307             "tab" : function(e){
43308                 this.collapse();
43309                 
43310                 if(this.fireEvent("specialkey", this, e)){
43311                     this.onViewClick(false);
43312                 }
43313                 
43314                 return true;
43315             },
43316
43317             scope : this,
43318
43319             doRelay : function(foo, bar, hname){
43320                 if(hname == 'down' || this.scope.isExpanded()){
43321                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43322                 }
43323                 return true;
43324             },
43325
43326             forceKeyDown: true
43327         });
43328         
43329         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43330         
43331     },
43332     
43333     initNumberEvent : function(e)
43334     {
43335         this.inputEl().on("keydown" , this.fireKey,  this);
43336         this.inputEl().on("focus", this.onFocus,  this);
43337         this.inputEl().on("blur", this.onBlur,  this);
43338         
43339         this.inputEl().relayEvent('keyup', this);
43340         
43341         if(this.indicator){
43342             this.indicator.addClass('invisible');
43343         }
43344  
43345         this.originalValue = this.getValue();
43346         
43347         if(this.validationEvent == 'keyup'){
43348             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43349             this.inputEl().on('keyup', this.filterValidation, this);
43350         }
43351         else if(this.validationEvent !== false){
43352             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43353         }
43354         
43355         if(this.selectOnFocus){
43356             this.on("focus", this.preFocus, this);
43357             
43358         }
43359         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43360             this.inputEl().on("keypress", this.filterKeys, this);
43361         } else {
43362             this.inputEl().relayEvent('keypress', this);
43363         }
43364         
43365         var allowed = "0123456789";
43366         
43367         if(this.allowDecimals){
43368             allowed += this.decimalSeparator;
43369         }
43370         
43371         if(this.allowNegative){
43372             allowed += "-";
43373         }
43374         
43375         if(this.thousandsDelimiter) {
43376             allowed += ",";
43377         }
43378         
43379         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43380         
43381         var keyPress = function(e){
43382             
43383             var k = e.getKey();
43384             
43385             var c = e.getCharCode();
43386             
43387             if(
43388                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43389                     allowed.indexOf(String.fromCharCode(c)) === -1
43390             ){
43391                 e.stopEvent();
43392                 return;
43393             }
43394             
43395             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43396                 return;
43397             }
43398             
43399             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43400                 e.stopEvent();
43401             }
43402         };
43403         
43404         this.inputEl().on("keypress", keyPress, this);
43405         
43406     },
43407     
43408     onTriggerClick : function(e)
43409     {   
43410         if(this.disabled){
43411             return;
43412         }
43413         
43414         this.page = 0;
43415         this.loadNext = false;
43416         
43417         if(this.isExpanded()){
43418             this.collapse();
43419             return;
43420         }
43421         
43422         this.hasFocus = true;
43423         
43424         if(this.triggerAction == 'all') {
43425             this.doQuery(this.allQuery, true);
43426             return;
43427         }
43428         
43429         this.doQuery(this.getRawValue());
43430     },
43431     
43432     getCurrency : function()
43433     {   
43434         var v = this.currencyEl().getValue();
43435         
43436         return v;
43437     },
43438     
43439     restrictHeight : function()
43440     {
43441         this.list.alignTo(this.currencyEl(), this.listAlign);
43442         this.list.alignTo(this.currencyEl(), this.listAlign);
43443     },
43444     
43445     onViewClick : function(view, doFocus, el, e)
43446     {
43447         var index = this.view.getSelectedIndexes()[0];
43448         
43449         var r = this.store.getAt(index);
43450         
43451         if(r){
43452             this.onSelect(r, index);
43453         }
43454     },
43455     
43456     onSelect : function(record, index){
43457         
43458         if(this.fireEvent('beforeselect', this, record, index) !== false){
43459         
43460             this.setFromCurrencyData(index > -1 ? record.data : false);
43461             
43462             this.collapse();
43463             
43464             this.fireEvent('select', this, record, index);
43465         }
43466     },
43467     
43468     setFromCurrencyData : function(o)
43469     {
43470         var currency = '';
43471         
43472         this.lastCurrency = o;
43473         
43474         if (this.currencyField) {
43475             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43476         } else {
43477             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43478         }
43479         
43480         this.lastSelectionText = currency;
43481         
43482         //setting default currency
43483         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43484             this.setCurrency(this.defaultCurrency);
43485             return;
43486         }
43487         
43488         this.setCurrency(currency);
43489     },
43490     
43491     setFromData : function(o)
43492     {
43493         var c = {};
43494         
43495         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43496         
43497         this.setFromCurrencyData(c);
43498         
43499         var value = '';
43500         
43501         if (this.name) {
43502             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43503         } else {
43504             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43505         }
43506         
43507         this.setValue(value);
43508         
43509     },
43510     
43511     setCurrency : function(v)
43512     {   
43513         this.currencyValue = v;
43514         
43515         if(this.rendered){
43516             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43517             this.validate();
43518         }
43519     },
43520     
43521     setValue : function(v)
43522     {
43523         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43524         
43525         this.value = v;
43526         
43527         if(this.rendered){
43528             
43529             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43530             
43531             this.inputEl().dom.value = (v == '') ? '' :
43532                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43533             
43534             if(!this.allowZero && v === '0') {
43535                 this.hiddenEl().dom.value = '';
43536                 this.inputEl().dom.value = '';
43537             }
43538             
43539             this.validate();
43540         }
43541     },
43542     
43543     getRawValue : function()
43544     {
43545         var v = this.inputEl().getValue();
43546         
43547         return v;
43548     },
43549     
43550     getValue : function()
43551     {
43552         return this.fixPrecision(this.parseValue(this.getRawValue()));
43553     },
43554     
43555     parseValue : function(value)
43556     {
43557         if(this.thousandsDelimiter) {
43558             value += "";
43559             r = new RegExp(",", "g");
43560             value = value.replace(r, "");
43561         }
43562         
43563         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43564         return isNaN(value) ? '' : value;
43565         
43566     },
43567     
43568     fixPrecision : function(value)
43569     {
43570         if(this.thousandsDelimiter) {
43571             value += "";
43572             r = new RegExp(",", "g");
43573             value = value.replace(r, "");
43574         }
43575         
43576         var nan = isNaN(value);
43577         
43578         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43579             return nan ? '' : value;
43580         }
43581         return parseFloat(value).toFixed(this.decimalPrecision);
43582     },
43583     
43584     decimalPrecisionFcn : function(v)
43585     {
43586         return Math.floor(v);
43587     },
43588     
43589     validateValue : function(value)
43590     {
43591         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43592             return false;
43593         }
43594         
43595         var num = this.parseValue(value);
43596         
43597         if(isNaN(num)){
43598             this.markInvalid(String.format(this.nanText, value));
43599             return false;
43600         }
43601         
43602         if(num < this.minValue){
43603             this.markInvalid(String.format(this.minText, this.minValue));
43604             return false;
43605         }
43606         
43607         if(num > this.maxValue){
43608             this.markInvalid(String.format(this.maxText, this.maxValue));
43609             return false;
43610         }
43611         
43612         return true;
43613     },
43614     
43615     validate : function()
43616     {
43617         if(this.disabled || this.allowBlank){
43618             this.markValid();
43619             return true;
43620         }
43621         
43622         var currency = this.getCurrency();
43623         
43624         if(this.validateValue(this.getRawValue()) && currency.length){
43625             this.markValid();
43626             return true;
43627         }
43628         
43629         this.markInvalid();
43630         return false;
43631     },
43632     
43633     getName: function()
43634     {
43635         return this.name;
43636     },
43637     
43638     beforeBlur : function()
43639     {
43640         if(!this.castInt){
43641             return;
43642         }
43643         
43644         var v = this.parseValue(this.getRawValue());
43645         
43646         if(v || v == 0){
43647             this.setValue(v);
43648         }
43649     },
43650     
43651     onBlur : function()
43652     {
43653         this.beforeBlur();
43654         
43655         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43656             //this.el.removeClass(this.focusClass);
43657         }
43658         
43659         this.hasFocus = false;
43660         
43661         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43662             this.validate();
43663         }
43664         
43665         var v = this.getValue();
43666         
43667         if(String(v) !== String(this.startValue)){
43668             this.fireEvent('change', this, v, this.startValue);
43669         }
43670         
43671         this.fireEvent("blur", this);
43672     },
43673     
43674     inputEl : function()
43675     {
43676         return this.el.select('.roo-money-amount-input', true).first();
43677     },
43678     
43679     currencyEl : function()
43680     {
43681         return this.el.select('.roo-money-currency-input', true).first();
43682     },
43683     
43684     hiddenEl : function()
43685     {
43686         return this.el.select('input.hidden-number-input',true).first();
43687     }
43688     
43689 });/**
43690  * @class Roo.bootstrap.BezierSignature
43691  * @extends Roo.bootstrap.Component
43692  * Bootstrap BezierSignature class
43693  * This script refer to:
43694  *    Title: Signature Pad
43695  *    Author: szimek
43696  *    Availability: https://github.com/szimek/signature_pad
43697  *
43698  * @constructor
43699  * Create a new BezierSignature
43700  * @param {Object} config The config object
43701  */
43702
43703 Roo.bootstrap.BezierSignature = function(config){
43704     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43705     this.addEvents({
43706         "resize" : true
43707     });
43708 };
43709
43710 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43711 {
43712      
43713     curve_data: [],
43714     
43715     is_empty: true,
43716     
43717     mouse_btn_down: true,
43718     
43719     /**
43720      * @cfg {int} canvas height
43721      */
43722     canvas_height: '200px',
43723     
43724     /**
43725      * @cfg {float|function} Radius of a single dot.
43726      */ 
43727     dot_size: false,
43728     
43729     /**
43730      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43731      */
43732     min_width: 0.5,
43733     
43734     /**
43735      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43736      */
43737     max_width: 2.5,
43738     
43739     /**
43740      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43741      */
43742     throttle: 16,
43743     
43744     /**
43745      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43746      */
43747     min_distance: 5,
43748     
43749     /**
43750      * @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.
43751      */
43752     bg_color: 'rgba(0, 0, 0, 0)',
43753     
43754     /**
43755      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43756      */
43757     dot_color: 'black',
43758     
43759     /**
43760      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43761      */ 
43762     velocity_filter_weight: 0.7,
43763     
43764     /**
43765      * @cfg {function} Callback when stroke begin. 
43766      */
43767     onBegin: false,
43768     
43769     /**
43770      * @cfg {function} Callback when stroke end.
43771      */
43772     onEnd: false,
43773     
43774     getAutoCreate : function()
43775     {
43776         var cls = 'roo-signature column';
43777         
43778         if(this.cls){
43779             cls += ' ' + this.cls;
43780         }
43781         
43782         var col_sizes = [
43783             'lg',
43784             'md',
43785             'sm',
43786             'xs'
43787         ];
43788         
43789         for(var i = 0; i < col_sizes.length; i++) {
43790             if(this[col_sizes[i]]) {
43791                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43792             }
43793         }
43794         
43795         var cfg = {
43796             tag: 'div',
43797             cls: cls,
43798             cn: [
43799                 {
43800                     tag: 'div',
43801                     cls: 'roo-signature-body',
43802                     cn: [
43803                         {
43804                             tag: 'canvas',
43805                             cls: 'roo-signature-body-canvas',
43806                             height: this.canvas_height,
43807                             width: this.canvas_width
43808                         }
43809                     ]
43810                 },
43811                 {
43812                     tag: 'input',
43813                     type: 'file',
43814                     style: 'display: none'
43815                 }
43816             ]
43817         };
43818         
43819         return cfg;
43820     },
43821     
43822     initEvents: function() 
43823     {
43824         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43825         
43826         var canvas = this.canvasEl();
43827         
43828         // mouse && touch event swapping...
43829         canvas.dom.style.touchAction = 'none';
43830         canvas.dom.style.msTouchAction = 'none';
43831         
43832         this.mouse_btn_down = false;
43833         canvas.on('mousedown', this._handleMouseDown, this);
43834         canvas.on('mousemove', this._handleMouseMove, this);
43835         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43836         
43837         if (window.PointerEvent) {
43838             canvas.on('pointerdown', this._handleMouseDown, this);
43839             canvas.on('pointermove', this._handleMouseMove, this);
43840             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43841         }
43842         
43843         if ('ontouchstart' in window) {
43844             canvas.on('touchstart', this._handleTouchStart, this);
43845             canvas.on('touchmove', this._handleTouchMove, this);
43846             canvas.on('touchend', this._handleTouchEnd, this);
43847         }
43848         
43849         Roo.EventManager.onWindowResize(this.resize, this, true);
43850         
43851         // file input event
43852         this.fileEl().on('change', this.uploadImage, this);
43853         
43854         this.clear();
43855         
43856         this.resize();
43857     },
43858     
43859     resize: function(){
43860         
43861         var canvas = this.canvasEl().dom;
43862         var ctx = this.canvasElCtx();
43863         var img_data = false;
43864         
43865         if(canvas.width > 0) {
43866             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43867         }
43868         // setting canvas width will clean img data
43869         canvas.width = 0;
43870         
43871         var style = window.getComputedStyle ? 
43872             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43873             
43874         var padding_left = parseInt(style.paddingLeft) || 0;
43875         var padding_right = parseInt(style.paddingRight) || 0;
43876         
43877         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43878         
43879         if(img_data) {
43880             ctx.putImageData(img_data, 0, 0);
43881         }
43882     },
43883     
43884     _handleMouseDown: function(e)
43885     {
43886         if (e.browserEvent.which === 1) {
43887             this.mouse_btn_down = true;
43888             this.strokeBegin(e);
43889         }
43890     },
43891     
43892     _handleMouseMove: function (e)
43893     {
43894         if (this.mouse_btn_down) {
43895             this.strokeMoveUpdate(e);
43896         }
43897     },
43898     
43899     _handleMouseUp: function (e)
43900     {
43901         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43902             this.mouse_btn_down = false;
43903             this.strokeEnd(e);
43904         }
43905     },
43906     
43907     _handleTouchStart: function (e) {
43908         
43909         e.preventDefault();
43910         if (e.browserEvent.targetTouches.length === 1) {
43911             // var touch = e.browserEvent.changedTouches[0];
43912             // this.strokeBegin(touch);
43913             
43914              this.strokeBegin(e); // assume e catching the correct xy...
43915         }
43916     },
43917     
43918     _handleTouchMove: function (e) {
43919         e.preventDefault();
43920         // var touch = event.targetTouches[0];
43921         // _this._strokeMoveUpdate(touch);
43922         this.strokeMoveUpdate(e);
43923     },
43924     
43925     _handleTouchEnd: function (e) {
43926         var wasCanvasTouched = e.target === this.canvasEl().dom;
43927         if (wasCanvasTouched) {
43928             e.preventDefault();
43929             // var touch = event.changedTouches[0];
43930             // _this._strokeEnd(touch);
43931             this.strokeEnd(e);
43932         }
43933     },
43934     
43935     reset: function () {
43936         this._lastPoints = [];
43937         this._lastVelocity = 0;
43938         this._lastWidth = (this.min_width + this.max_width) / 2;
43939         this.canvasElCtx().fillStyle = this.dot_color;
43940     },
43941     
43942     strokeMoveUpdate: function(e)
43943     {
43944         this.strokeUpdate(e);
43945         
43946         if (this.throttle) {
43947             this.throttleStroke(this.strokeUpdate, this.throttle);
43948         }
43949         else {
43950             this.strokeUpdate(e);
43951         }
43952     },
43953     
43954     strokeBegin: function(e)
43955     {
43956         var newPointGroup = {
43957             color: this.dot_color,
43958             points: []
43959         };
43960         
43961         if (typeof this.onBegin === 'function') {
43962             this.onBegin(e);
43963         }
43964         
43965         this.curve_data.push(newPointGroup);
43966         this.reset();
43967         this.strokeUpdate(e);
43968     },
43969     
43970     strokeUpdate: function(e)
43971     {
43972         var rect = this.canvasEl().dom.getBoundingClientRect();
43973         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43974         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43975         var lastPoints = lastPointGroup.points;
43976         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43977         var isLastPointTooClose = lastPoint
43978             ? point.distanceTo(lastPoint) <= this.min_distance
43979             : false;
43980         var color = lastPointGroup.color;
43981         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43982             var curve = this.addPoint(point);
43983             if (!lastPoint) {
43984                 this.drawDot({color: color, point: point});
43985             }
43986             else if (curve) {
43987                 this.drawCurve({color: color, curve: curve});
43988             }
43989             lastPoints.push({
43990                 time: point.time,
43991                 x: point.x,
43992                 y: point.y
43993             });
43994         }
43995     },
43996     
43997     strokeEnd: function(e)
43998     {
43999         this.strokeUpdate(e);
44000         if (typeof this.onEnd === 'function') {
44001             this.onEnd(e);
44002         }
44003     },
44004     
44005     addPoint:  function (point) {
44006         var _lastPoints = this._lastPoints;
44007         _lastPoints.push(point);
44008         if (_lastPoints.length > 2) {
44009             if (_lastPoints.length === 3) {
44010                 _lastPoints.unshift(_lastPoints[0]);
44011             }
44012             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44013             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44014             _lastPoints.shift();
44015             return curve;
44016         }
44017         return null;
44018     },
44019     
44020     calculateCurveWidths: function (startPoint, endPoint) {
44021         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44022             (1 - this.velocity_filter_weight) * this._lastVelocity;
44023
44024         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44025         var widths = {
44026             end: newWidth,
44027             start: this._lastWidth
44028         };
44029         
44030         this._lastVelocity = velocity;
44031         this._lastWidth = newWidth;
44032         return widths;
44033     },
44034     
44035     drawDot: function (_a) {
44036         var color = _a.color, point = _a.point;
44037         var ctx = this.canvasElCtx();
44038         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44039         ctx.beginPath();
44040         this.drawCurveSegment(point.x, point.y, width);
44041         ctx.closePath();
44042         ctx.fillStyle = color;
44043         ctx.fill();
44044     },
44045     
44046     drawCurve: function (_a) {
44047         var color = _a.color, curve = _a.curve;
44048         var ctx = this.canvasElCtx();
44049         var widthDelta = curve.endWidth - curve.startWidth;
44050         var drawSteps = Math.floor(curve.length()) * 2;
44051         ctx.beginPath();
44052         ctx.fillStyle = color;
44053         for (var i = 0; i < drawSteps; i += 1) {
44054         var t = i / drawSteps;
44055         var tt = t * t;
44056         var ttt = tt * t;
44057         var u = 1 - t;
44058         var uu = u * u;
44059         var uuu = uu * u;
44060         var x = uuu * curve.startPoint.x;
44061         x += 3 * uu * t * curve.control1.x;
44062         x += 3 * u * tt * curve.control2.x;
44063         x += ttt * curve.endPoint.x;
44064         var y = uuu * curve.startPoint.y;
44065         y += 3 * uu * t * curve.control1.y;
44066         y += 3 * u * tt * curve.control2.y;
44067         y += ttt * curve.endPoint.y;
44068         var width = curve.startWidth + ttt * widthDelta;
44069         this.drawCurveSegment(x, y, width);
44070         }
44071         ctx.closePath();
44072         ctx.fill();
44073     },
44074     
44075     drawCurveSegment: function (x, y, width) {
44076         var ctx = this.canvasElCtx();
44077         ctx.moveTo(x, y);
44078         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44079         this.is_empty = false;
44080     },
44081     
44082     clear: function()
44083     {
44084         var ctx = this.canvasElCtx();
44085         var canvas = this.canvasEl().dom;
44086         ctx.fillStyle = this.bg_color;
44087         ctx.clearRect(0, 0, canvas.width, canvas.height);
44088         ctx.fillRect(0, 0, canvas.width, canvas.height);
44089         this.curve_data = [];
44090         this.reset();
44091         this.is_empty = true;
44092     },
44093     
44094     fileEl: function()
44095     {
44096         return  this.el.select('input',true).first();
44097     },
44098     
44099     canvasEl: function()
44100     {
44101         return this.el.select('canvas',true).first();
44102     },
44103     
44104     canvasElCtx: function()
44105     {
44106         return this.el.select('canvas',true).first().dom.getContext('2d');
44107     },
44108     
44109     getImage: function(type)
44110     {
44111         if(this.is_empty) {
44112             return false;
44113         }
44114         
44115         // encryption ?
44116         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44117     },
44118     
44119     drawFromImage: function(img_src)
44120     {
44121         var img = new Image();
44122         
44123         img.onload = function(){
44124             this.canvasElCtx().drawImage(img, 0, 0);
44125         }.bind(this);
44126         
44127         img.src = img_src;
44128         
44129         this.is_empty = false;
44130     },
44131     
44132     selectImage: function()
44133     {
44134         this.fileEl().dom.click();
44135     },
44136     
44137     uploadImage: function(e)
44138     {
44139         var reader = new FileReader();
44140         
44141         reader.onload = function(e){
44142             var img = new Image();
44143             img.onload = function(){
44144                 this.reset();
44145                 this.canvasElCtx().drawImage(img, 0, 0);
44146             }.bind(this);
44147             img.src = e.target.result;
44148         }.bind(this);
44149         
44150         reader.readAsDataURL(e.target.files[0]);
44151     },
44152     
44153     // Bezier Point Constructor
44154     Point: (function () {
44155         function Point(x, y, time) {
44156             this.x = x;
44157             this.y = y;
44158             this.time = time || Date.now();
44159         }
44160         Point.prototype.distanceTo = function (start) {
44161             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44162         };
44163         Point.prototype.equals = function (other) {
44164             return this.x === other.x && this.y === other.y && this.time === other.time;
44165         };
44166         Point.prototype.velocityFrom = function (start) {
44167             return this.time !== start.time
44168             ? this.distanceTo(start) / (this.time - start.time)
44169             : 0;
44170         };
44171         return Point;
44172     }()),
44173     
44174     
44175     // Bezier Constructor
44176     Bezier: (function () {
44177         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44178             this.startPoint = startPoint;
44179             this.control2 = control2;
44180             this.control1 = control1;
44181             this.endPoint = endPoint;
44182             this.startWidth = startWidth;
44183             this.endWidth = endWidth;
44184         }
44185         Bezier.fromPoints = function (points, widths, scope) {
44186             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44187             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44188             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44189         };
44190         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44191             var dx1 = s1.x - s2.x;
44192             var dy1 = s1.y - s2.y;
44193             var dx2 = s2.x - s3.x;
44194             var dy2 = s2.y - s3.y;
44195             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44196             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44197             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44198             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44199             var dxm = m1.x - m2.x;
44200             var dym = m1.y - m2.y;
44201             var k = l2 / (l1 + l2);
44202             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44203             var tx = s2.x - cm.x;
44204             var ty = s2.y - cm.y;
44205             return {
44206                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44207                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44208             };
44209         };
44210         Bezier.prototype.length = function () {
44211             var steps = 10;
44212             var length = 0;
44213             var px;
44214             var py;
44215             for (var i = 0; i <= steps; i += 1) {
44216                 var t = i / steps;
44217                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44218                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44219                 if (i > 0) {
44220                     var xdiff = cx - px;
44221                     var ydiff = cy - py;
44222                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44223                 }
44224                 px = cx;
44225                 py = cy;
44226             }
44227             return length;
44228         };
44229         Bezier.prototype.point = function (t, start, c1, c2, end) {
44230             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44231             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44232             + (3.0 * c2 * (1.0 - t) * t * t)
44233             + (end * t * t * t);
44234         };
44235         return Bezier;
44236     }()),
44237     
44238     throttleStroke: function(fn, wait) {
44239       if (wait === void 0) { wait = 250; }
44240       var previous = 0;
44241       var timeout = null;
44242       var result;
44243       var storedContext;
44244       var storedArgs;
44245       var later = function () {
44246           previous = Date.now();
44247           timeout = null;
44248           result = fn.apply(storedContext, storedArgs);
44249           if (!timeout) {
44250               storedContext = null;
44251               storedArgs = [];
44252           }
44253       };
44254       return function wrapper() {
44255           var args = [];
44256           for (var _i = 0; _i < arguments.length; _i++) {
44257               args[_i] = arguments[_i];
44258           }
44259           var now = Date.now();
44260           var remaining = wait - (now - previous);
44261           storedContext = this;
44262           storedArgs = args;
44263           if (remaining <= 0 || remaining > wait) {
44264               if (timeout) {
44265                   clearTimeout(timeout);
44266                   timeout = null;
44267               }
44268               previous = now;
44269               result = fn.apply(storedContext, storedArgs);
44270               if (!timeout) {
44271                   storedContext = null;
44272                   storedArgs = [];
44273               }
44274           }
44275           else if (!timeout) {
44276               timeout = window.setTimeout(later, remaining);
44277           }
44278           return result;
44279       };
44280   }
44281   
44282 });
44283
44284  
44285
44286