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     headerEl : false,
19656     contentEl : false,
19657     
19658     
19659     getChildContainer : function()
19660     {
19661         return this.contentEl;
19662         
19663     },
19664     getPopoverHeader : function()
19665     {
19666         this.title = true; // flag not to hide it..
19667         return this.headerEl
19668     },
19669     
19670     
19671     getAutoCreate : function(){
19672          
19673         var cfg = {
19674            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19675            style: 'display:block',
19676            cn : [
19677                 {
19678                     cls : 'arrow'
19679                 },
19680                 {
19681                     cls : 'popover-inner ',
19682                     cn : [
19683                         {
19684                             tag: 'h3',
19685                             cls: 'popover-title popover-header',
19686                             html : this.title || ''
19687                         },
19688                         {
19689                             cls : 'popover-content popover-body'  + this.cls,
19690                             html : this.html || ''
19691                         }
19692                     ]
19693                     
19694                 }
19695            ]
19696         };
19697         
19698         return cfg;
19699     },
19700     /**
19701      * @param {string} the title
19702      */
19703     setTitle: function(str)
19704     {
19705         this.title = str;
19706         if (this.el) {
19707             this.headerEl.dom.innerHTML = str;
19708         }
19709         
19710     },
19711     /**
19712      * @param {string} the body content
19713      */
19714     setContent: function(str)
19715     {
19716         this.html = str;
19717         if (this.contentEl) {
19718             this.contentEl.dom.innerHTML = str;
19719         }
19720         
19721     },
19722     // as it get's added to the bottom of the page.
19723     onRender : function(ct, position)
19724     {
19725         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19726         if(!this.el){
19727             var cfg = Roo.apply({},  this.getAutoCreate());
19728             cfg.id = Roo.id();
19729             
19730             if (this.cls) {
19731                 cfg.cls += ' ' + this.cls;
19732             }
19733             if (this.style) {
19734                 cfg.style = this.style;
19735             }
19736             //Roo.log("adding to ");
19737             this.el = Roo.get(document.body).createChild(cfg, position);
19738 //            Roo.log(this.el);
19739         }
19740         
19741         var nitems = [];
19742         if(typeof(this.items) != 'undefined'){
19743             var items = this.items;
19744             delete this.items;
19745
19746             for(var i =0;i < items.length;i++) {
19747                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19748             }
19749         }
19750
19751         this.items = nitems;
19752         
19753         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19754         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19755         
19756         
19757         this.contentEl = this.el.select('.popover-content',true).first();
19758         this.headerEl =  this.el.select('.popover-header',true).first();
19759         
19760         this.initEvents();
19761     },
19762     
19763     resizeMask : function()
19764     {
19765         this.maskEl.setSize(
19766             Roo.lib.Dom.getViewWidth(true),
19767             Roo.lib.Dom.getViewHeight(true)
19768         );
19769     },
19770     
19771     initEvents : function()
19772     {
19773         
19774         if (!this.modal) { 
19775             Roo.bootstrap.Popover.register(this);
19776         }
19777          
19778         
19779         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY); // probably not needed as it's default in BS4
19780         this.el.enableDisplayMode('block');
19781         this.el.hide();
19782         if (this.over === false && !this.parent()) {
19783             return; 
19784         }
19785         if (this.triggers === false) {
19786             return;
19787         }
19788          
19789         // support parent
19790         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19791         var triggers = this.trigger ? this.trigger.split(' ') : [];
19792         Roo.each(triggers, function(trigger) {
19793         
19794             if (trigger == 'click') {
19795                 on_el.on('click', this.toggle, this);
19796             } else if (trigger != 'manual') {
19797                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19798                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19799       
19800                 on_el.on(eventIn  ,this.enter, this);
19801                 on_el.on(eventOut, this.leave, this);
19802             }
19803         }, this);
19804         
19805     },
19806     
19807     
19808     // private
19809     timeout : null,
19810     hoverState : null,
19811     
19812     toggle : function () {
19813         this.hoverState == 'in' ? this.leave() : this.enter();
19814     },
19815     
19816     enter : function () {
19817         
19818         clearTimeout(this.timeout);
19819     
19820         this.hoverState = 'in';
19821     
19822         if (!this.delay || !this.delay.show) {
19823             this.show();
19824             return;
19825         }
19826         var _t = this;
19827         this.timeout = setTimeout(function () {
19828             if (_t.hoverState == 'in') {
19829                 _t.show();
19830             }
19831         }, this.delay.show)
19832     },
19833     
19834     leave : function() {
19835         clearTimeout(this.timeout);
19836     
19837         this.hoverState = 'out';
19838     
19839         if (!this.delay || !this.delay.hide) {
19840             this.hide();
19841             return;
19842         }
19843         var _t = this;
19844         this.timeout = setTimeout(function () {
19845             if (_t.hoverState == 'out') {
19846                 _t.hide();
19847             }
19848         }, this.delay.hide)
19849     },
19850     /**
19851      * Show the popover
19852      * @param {Roo.Element|string|false} - element to align and point to.
19853      */
19854     show : function (on_el)
19855     {
19856         
19857         on_el = on_el || false; // default to false
19858         if (!on_el) {
19859             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19860                 on_el = this.parent().el;
19861             } else if (this.over) {
19862                 Roo.get(this.over);
19863             }
19864             
19865         }
19866         
19867         if (!this.el) {
19868             this.render(document.body);
19869         }
19870         
19871         
19872         this.el.removeClass([
19873             'fade','top','bottom', 'left', 'right','in',
19874             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19875         ]);
19876         
19877         if (this.title === false) {
19878             this.headerEl.hide();
19879         }
19880         
19881         
19882         var placement = typeof this.placement == 'function' ?
19883             this.placement.call(this, this.el, on_el) :
19884             this.placement;
19885             
19886         /*
19887         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19888         
19889         // I think  'auto right' - but 
19890         
19891         var autoPlace = autoToken.test(placement);
19892         if (autoPlace) {
19893             placement = placement.replace(autoToken, '') || 'top';
19894         }
19895         */
19896         
19897         
19898         this.el.show();
19899         this.el.dom.style.display='block';
19900         
19901         //this.el.appendTo(on_el);
19902         
19903         var p = this.getPosition();
19904         var box = this.el.getBox();
19905         
19906         
19907         var align = Roo.bootstrap.Popover.alignment[placement];
19908         this.el.addClass(align[2]);
19909
19910 //        Roo.log(align);
19911
19912         if (on_el) {
19913             this.el.alignTo(on_el, align[0],align[1]);
19914         } else {
19915             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19916             var es = this.el.getSize();
19917             var x = Roo.lib.Dom.getViewWidth()/2;
19918             var y = Roo.lib.Dom.getViewHeight()/2;
19919             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19920             
19921         }
19922
19923         
19924         //var arrow = this.el.select('.arrow',true).first();
19925         //arrow.set(align[2], 
19926         
19927         this.el.addClass('in');
19928         
19929         
19930         if (this.el.hasClass('fade')) {
19931             // fade it?
19932         }
19933         
19934         this.hoverState = 'in';
19935         
19936         if (this.modal) {
19937             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19938             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19939             this.maskEl.dom.style.display = 'block';
19940             this.maskEl.addClass('show');
19941         }
19942         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19943
19944         
19945         
19946         this.fireEvent('show', this);
19947         
19948     },
19949     hide : function()
19950     {
19951         this.el.setXY([0,0]);
19952         this.el.removeClass('in');
19953         this.el.hide();
19954         this.hoverState = null;
19955         this.maskEl.hide(); // always..
19956         this.fireEvent('hide', this);
19957     }
19958     
19959 });
19960
19961
19962 Roo.apply(Roo.bootstrap.Popover, {
19963
19964     alignment : {
19965         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19966         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19967         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19968         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19969     },
19970     
19971     zIndex : 20001,
19972
19973     clickHander : false,
19974     
19975
19976     onMouseDown : function(e)
19977     {
19978         if (!e.getTarget(".roo-popover")) {
19979             this.hideAll();
19980         }
19981          
19982     },
19983     
19984     popups : [],
19985     
19986     register : function(popup)
19987     {
19988         if (!Roo.bootstrap.Popover.clickHandler) {
19989             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19990         }
19991         // hide other popups.
19992         this.hideAll();
19993         this.popups.push(popup);
19994     },
19995     hideAll : function()
19996     {
19997         this.popups.forEach(function(p) {
19998             p.hide();
19999         });
20000     }
20001
20002 });/*
20003  * - LGPL
20004  *
20005  * Progress
20006  * 
20007  */
20008
20009 /**
20010  * @class Roo.bootstrap.Progress
20011  * @extends Roo.bootstrap.Component
20012  * Bootstrap Progress class
20013  * @cfg {Boolean} striped striped of the progress bar
20014  * @cfg {Boolean} active animated of the progress bar
20015  * 
20016  * 
20017  * @constructor
20018  * Create a new Progress
20019  * @param {Object} config The config object
20020  */
20021
20022 Roo.bootstrap.Progress = function(config){
20023     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20024 };
20025
20026 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20027     
20028     striped : false,
20029     active: false,
20030     
20031     getAutoCreate : function(){
20032         var cfg = {
20033             tag: 'div',
20034             cls: 'progress'
20035         };
20036         
20037         
20038         if(this.striped){
20039             cfg.cls += ' progress-striped';
20040         }
20041       
20042         if(this.active){
20043             cfg.cls += ' active';
20044         }
20045         
20046         
20047         return cfg;
20048     }
20049    
20050 });
20051
20052  
20053
20054  /*
20055  * - LGPL
20056  *
20057  * ProgressBar
20058  * 
20059  */
20060
20061 /**
20062  * @class Roo.bootstrap.ProgressBar
20063  * @extends Roo.bootstrap.Component
20064  * Bootstrap ProgressBar class
20065  * @cfg {Number} aria_valuenow aria-value now
20066  * @cfg {Number} aria_valuemin aria-value min
20067  * @cfg {Number} aria_valuemax aria-value max
20068  * @cfg {String} label label for the progress bar
20069  * @cfg {String} panel (success | info | warning | danger )
20070  * @cfg {String} role role of the progress bar
20071  * @cfg {String} sr_only text
20072  * 
20073  * 
20074  * @constructor
20075  * Create a new ProgressBar
20076  * @param {Object} config The config object
20077  */
20078
20079 Roo.bootstrap.ProgressBar = function(config){
20080     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20081 };
20082
20083 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20084     
20085     aria_valuenow : 0,
20086     aria_valuemin : 0,
20087     aria_valuemax : 100,
20088     label : false,
20089     panel : false,
20090     role : false,
20091     sr_only: false,
20092     
20093     getAutoCreate : function()
20094     {
20095         
20096         var cfg = {
20097             tag: 'div',
20098             cls: 'progress-bar',
20099             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20100         };
20101         
20102         if(this.sr_only){
20103             cfg.cn = {
20104                 tag: 'span',
20105                 cls: 'sr-only',
20106                 html: this.sr_only
20107             }
20108         }
20109         
20110         if(this.role){
20111             cfg.role = this.role;
20112         }
20113         
20114         if(this.aria_valuenow){
20115             cfg['aria-valuenow'] = this.aria_valuenow;
20116         }
20117         
20118         if(this.aria_valuemin){
20119             cfg['aria-valuemin'] = this.aria_valuemin;
20120         }
20121         
20122         if(this.aria_valuemax){
20123             cfg['aria-valuemax'] = this.aria_valuemax;
20124         }
20125         
20126         if(this.label && !this.sr_only){
20127             cfg.html = this.label;
20128         }
20129         
20130         if(this.panel){
20131             cfg.cls += ' progress-bar-' + this.panel;
20132         }
20133         
20134         return cfg;
20135     },
20136     
20137     update : function(aria_valuenow)
20138     {
20139         this.aria_valuenow = aria_valuenow;
20140         
20141         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20142     }
20143    
20144 });
20145
20146  
20147
20148  /*
20149  * - LGPL
20150  *
20151  * column
20152  * 
20153  */
20154
20155 /**
20156  * @class Roo.bootstrap.TabGroup
20157  * @extends Roo.bootstrap.Column
20158  * Bootstrap Column class
20159  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20160  * @cfg {Boolean} carousel true to make the group behave like a carousel
20161  * @cfg {Boolean} bullets show bullets for the panels
20162  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20163  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20164  * @cfg {Boolean} showarrow (true|false) show arrow default true
20165  * 
20166  * @constructor
20167  * Create a new TabGroup
20168  * @param {Object} config The config object
20169  */
20170
20171 Roo.bootstrap.TabGroup = function(config){
20172     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20173     if (!this.navId) {
20174         this.navId = Roo.id();
20175     }
20176     this.tabs = [];
20177     Roo.bootstrap.TabGroup.register(this);
20178     
20179 };
20180
20181 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20182     
20183     carousel : false,
20184     transition : false,
20185     bullets : 0,
20186     timer : 0,
20187     autoslide : false,
20188     slideFn : false,
20189     slideOnTouch : false,
20190     showarrow : true,
20191     
20192     getAutoCreate : function()
20193     {
20194         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20195         
20196         cfg.cls += ' tab-content';
20197         
20198         if (this.carousel) {
20199             cfg.cls += ' carousel slide';
20200             
20201             cfg.cn = [{
20202                cls : 'carousel-inner',
20203                cn : []
20204             }];
20205         
20206             if(this.bullets  && !Roo.isTouch){
20207                 
20208                 var bullets = {
20209                     cls : 'carousel-bullets',
20210                     cn : []
20211                 };
20212                
20213                 if(this.bullets_cls){
20214                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20215                 }
20216                 
20217                 bullets.cn.push({
20218                     cls : 'clear'
20219                 });
20220                 
20221                 cfg.cn[0].cn.push(bullets);
20222             }
20223             
20224             if(this.showarrow){
20225                 cfg.cn[0].cn.push({
20226                     tag : 'div',
20227                     class : 'carousel-arrow',
20228                     cn : [
20229                         {
20230                             tag : 'div',
20231                             class : 'carousel-prev',
20232                             cn : [
20233                                 {
20234                                     tag : 'i',
20235                                     class : 'fa fa-chevron-left'
20236                                 }
20237                             ]
20238                         },
20239                         {
20240                             tag : 'div',
20241                             class : 'carousel-next',
20242                             cn : [
20243                                 {
20244                                     tag : 'i',
20245                                     class : 'fa fa-chevron-right'
20246                                 }
20247                             ]
20248                         }
20249                     ]
20250                 });
20251             }
20252             
20253         }
20254         
20255         return cfg;
20256     },
20257     
20258     initEvents:  function()
20259     {
20260 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20261 //            this.el.on("touchstart", this.onTouchStart, this);
20262 //        }
20263         
20264         if(this.autoslide){
20265             var _this = this;
20266             
20267             this.slideFn = window.setInterval(function() {
20268                 _this.showPanelNext();
20269             }, this.timer);
20270         }
20271         
20272         if(this.showarrow){
20273             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20274             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20275         }
20276         
20277         
20278     },
20279     
20280 //    onTouchStart : function(e, el, o)
20281 //    {
20282 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20283 //            return;
20284 //        }
20285 //        
20286 //        this.showPanelNext();
20287 //    },
20288     
20289     
20290     getChildContainer : function()
20291     {
20292         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20293     },
20294     
20295     /**
20296     * register a Navigation item
20297     * @param {Roo.bootstrap.NavItem} the navitem to add
20298     */
20299     register : function(item)
20300     {
20301         this.tabs.push( item);
20302         item.navId = this.navId; // not really needed..
20303         this.addBullet();
20304     
20305     },
20306     
20307     getActivePanel : function()
20308     {
20309         var r = false;
20310         Roo.each(this.tabs, function(t) {
20311             if (t.active) {
20312                 r = t;
20313                 return false;
20314             }
20315             return null;
20316         });
20317         return r;
20318         
20319     },
20320     getPanelByName : function(n)
20321     {
20322         var r = false;
20323         Roo.each(this.tabs, function(t) {
20324             if (t.tabId == n) {
20325                 r = t;
20326                 return false;
20327             }
20328             return null;
20329         });
20330         return r;
20331     },
20332     indexOfPanel : function(p)
20333     {
20334         var r = false;
20335         Roo.each(this.tabs, function(t,i) {
20336             if (t.tabId == p.tabId) {
20337                 r = i;
20338                 return false;
20339             }
20340             return null;
20341         });
20342         return r;
20343     },
20344     /**
20345      * show a specific panel
20346      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20347      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20348      */
20349     showPanel : function (pan)
20350     {
20351         if(this.transition || typeof(pan) == 'undefined'){
20352             Roo.log("waiting for the transitionend");
20353             return false;
20354         }
20355         
20356         if (typeof(pan) == 'number') {
20357             pan = this.tabs[pan];
20358         }
20359         
20360         if (typeof(pan) == 'string') {
20361             pan = this.getPanelByName(pan);
20362         }
20363         
20364         var cur = this.getActivePanel();
20365         
20366         if(!pan || !cur){
20367             Roo.log('pan or acitve pan is undefined');
20368             return false;
20369         }
20370         
20371         if (pan.tabId == this.getActivePanel().tabId) {
20372             return true;
20373         }
20374         
20375         if (false === cur.fireEvent('beforedeactivate')) {
20376             return false;
20377         }
20378         
20379         if(this.bullets > 0 && !Roo.isTouch){
20380             this.setActiveBullet(this.indexOfPanel(pan));
20381         }
20382         
20383         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20384             
20385             //class="carousel-item carousel-item-next carousel-item-left"
20386             
20387             this.transition = true;
20388             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20389             var lr = dir == 'next' ? 'left' : 'right';
20390             pan.el.addClass(dir); // or prev
20391             pan.el.addClass('carousel-item-' + dir); // or prev
20392             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20393             cur.el.addClass(lr); // or right
20394             pan.el.addClass(lr);
20395             cur.el.addClass('carousel-item-' +lr); // or right
20396             pan.el.addClass('carousel-item-' +lr);
20397             
20398             
20399             var _this = this;
20400             cur.el.on('transitionend', function() {
20401                 Roo.log("trans end?");
20402                 
20403                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20404                 pan.setActive(true);
20405                 
20406                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20407                 cur.setActive(false);
20408                 
20409                 _this.transition = false;
20410                 
20411             }, this, { single:  true } );
20412             
20413             return true;
20414         }
20415         
20416         cur.setActive(false);
20417         pan.setActive(true);
20418         
20419         return true;
20420         
20421     },
20422     showPanelNext : function()
20423     {
20424         var i = this.indexOfPanel(this.getActivePanel());
20425         
20426         if (i >= this.tabs.length - 1 && !this.autoslide) {
20427             return;
20428         }
20429         
20430         if (i >= this.tabs.length - 1 && this.autoslide) {
20431             i = -1;
20432         }
20433         
20434         this.showPanel(this.tabs[i+1]);
20435     },
20436     
20437     showPanelPrev : function()
20438     {
20439         var i = this.indexOfPanel(this.getActivePanel());
20440         
20441         if (i  < 1 && !this.autoslide) {
20442             return;
20443         }
20444         
20445         if (i < 1 && this.autoslide) {
20446             i = this.tabs.length;
20447         }
20448         
20449         this.showPanel(this.tabs[i-1]);
20450     },
20451     
20452     
20453     addBullet: function()
20454     {
20455         if(!this.bullets || Roo.isTouch){
20456             return;
20457         }
20458         var ctr = this.el.select('.carousel-bullets',true).first();
20459         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20460         var bullet = ctr.createChild({
20461             cls : 'bullet bullet-' + i
20462         },ctr.dom.lastChild);
20463         
20464         
20465         var _this = this;
20466         
20467         bullet.on('click', (function(e, el, o, ii, t){
20468
20469             e.preventDefault();
20470
20471             this.showPanel(ii);
20472
20473             if(this.autoslide && this.slideFn){
20474                 clearInterval(this.slideFn);
20475                 this.slideFn = window.setInterval(function() {
20476                     _this.showPanelNext();
20477                 }, this.timer);
20478             }
20479
20480         }).createDelegate(this, [i, bullet], true));
20481                 
20482         
20483     },
20484      
20485     setActiveBullet : function(i)
20486     {
20487         if(Roo.isTouch){
20488             return;
20489         }
20490         
20491         Roo.each(this.el.select('.bullet', true).elements, function(el){
20492             el.removeClass('selected');
20493         });
20494
20495         var bullet = this.el.select('.bullet-' + i, true).first();
20496         
20497         if(!bullet){
20498             return;
20499         }
20500         
20501         bullet.addClass('selected');
20502     }
20503     
20504     
20505   
20506 });
20507
20508  
20509
20510  
20511  
20512 Roo.apply(Roo.bootstrap.TabGroup, {
20513     
20514     groups: {},
20515      /**
20516     * register a Navigation Group
20517     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20518     */
20519     register : function(navgrp)
20520     {
20521         this.groups[navgrp.navId] = navgrp;
20522         
20523     },
20524     /**
20525     * fetch a Navigation Group based on the navigation ID
20526     * if one does not exist , it will get created.
20527     * @param {string} the navgroup to add
20528     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20529     */
20530     get: function(navId) {
20531         if (typeof(this.groups[navId]) == 'undefined') {
20532             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20533         }
20534         return this.groups[navId] ;
20535     }
20536     
20537     
20538     
20539 });
20540
20541  /*
20542  * - LGPL
20543  *
20544  * TabPanel
20545  * 
20546  */
20547
20548 /**
20549  * @class Roo.bootstrap.TabPanel
20550  * @extends Roo.bootstrap.Component
20551  * Bootstrap TabPanel class
20552  * @cfg {Boolean} active panel active
20553  * @cfg {String} html panel content
20554  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20555  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20556  * @cfg {String} href click to link..
20557  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20558  * 
20559  * 
20560  * @constructor
20561  * Create a new TabPanel
20562  * @param {Object} config The config object
20563  */
20564
20565 Roo.bootstrap.TabPanel = function(config){
20566     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20567     this.addEvents({
20568         /**
20569              * @event changed
20570              * Fires when the active status changes
20571              * @param {Roo.bootstrap.TabPanel} this
20572              * @param {Boolean} state the new state
20573             
20574          */
20575         'changed': true,
20576         /**
20577              * @event beforedeactivate
20578              * Fires before a tab is de-activated - can be used to do validation on a form.
20579              * @param {Roo.bootstrap.TabPanel} this
20580              * @return {Boolean} false if there is an error
20581             
20582          */
20583         'beforedeactivate': true
20584      });
20585     
20586     this.tabId = this.tabId || Roo.id();
20587   
20588 };
20589
20590 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20591     
20592     active: false,
20593     html: false,
20594     tabId: false,
20595     navId : false,
20596     href : '',
20597     touchSlide : false,
20598     getAutoCreate : function(){
20599         
20600         
20601         var cfg = {
20602             tag: 'div',
20603             // item is needed for carousel - not sure if it has any effect otherwise
20604             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20605             html: this.html || ''
20606         };
20607         
20608         if(this.active){
20609             cfg.cls += ' active';
20610         }
20611         
20612         if(this.tabId){
20613             cfg.tabId = this.tabId;
20614         }
20615         
20616         
20617         
20618         return cfg;
20619     },
20620     
20621     initEvents:  function()
20622     {
20623         var p = this.parent();
20624         
20625         this.navId = this.navId || p.navId;
20626         
20627         if (typeof(this.navId) != 'undefined') {
20628             // not really needed.. but just in case.. parent should be a NavGroup.
20629             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20630             
20631             tg.register(this);
20632             
20633             var i = tg.tabs.length - 1;
20634             
20635             if(this.active && tg.bullets > 0 && i < tg.bullets){
20636                 tg.setActiveBullet(i);
20637             }
20638         }
20639         
20640         this.el.on('click', this.onClick, this);
20641         
20642         if(Roo.isTouch && this.touchSlide){
20643             this.el.on("touchstart", this.onTouchStart, this);
20644             this.el.on("touchmove", this.onTouchMove, this);
20645             this.el.on("touchend", this.onTouchEnd, this);
20646         }
20647         
20648     },
20649     
20650     onRender : function(ct, position)
20651     {
20652         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20653     },
20654     
20655     setActive : function(state)
20656     {
20657         Roo.log("panel - set active " + this.tabId + "=" + state);
20658         
20659         this.active = state;
20660         if (!state) {
20661             this.el.removeClass('active');
20662             
20663         } else  if (!this.el.hasClass('active')) {
20664             this.el.addClass('active');
20665         }
20666         
20667         this.fireEvent('changed', this, state);
20668     },
20669     
20670     onClick : function(e)
20671     {
20672         e.preventDefault();
20673         
20674         if(!this.href.length){
20675             return;
20676         }
20677         
20678         window.location.href = this.href;
20679     },
20680     
20681     startX : 0,
20682     startY : 0,
20683     endX : 0,
20684     endY : 0,
20685     swiping : false,
20686     
20687     onTouchStart : function(e)
20688     {
20689         this.swiping = false;
20690         
20691         this.startX = e.browserEvent.touches[0].clientX;
20692         this.startY = e.browserEvent.touches[0].clientY;
20693     },
20694     
20695     onTouchMove : function(e)
20696     {
20697         this.swiping = true;
20698         
20699         this.endX = e.browserEvent.touches[0].clientX;
20700         this.endY = e.browserEvent.touches[0].clientY;
20701     },
20702     
20703     onTouchEnd : function(e)
20704     {
20705         if(!this.swiping){
20706             this.onClick(e);
20707             return;
20708         }
20709         
20710         var tabGroup = this.parent();
20711         
20712         if(this.endX > this.startX){ // swiping right
20713             tabGroup.showPanelPrev();
20714             return;
20715         }
20716         
20717         if(this.startX > this.endX){ // swiping left
20718             tabGroup.showPanelNext();
20719             return;
20720         }
20721     }
20722     
20723     
20724 });
20725  
20726
20727  
20728
20729  /*
20730  * - LGPL
20731  *
20732  * DateField
20733  * 
20734  */
20735
20736 /**
20737  * @class Roo.bootstrap.DateField
20738  * @extends Roo.bootstrap.Input
20739  * Bootstrap DateField class
20740  * @cfg {Number} weekStart default 0
20741  * @cfg {String} viewMode default empty, (months|years)
20742  * @cfg {String} minViewMode default empty, (months|years)
20743  * @cfg {Number} startDate default -Infinity
20744  * @cfg {Number} endDate default Infinity
20745  * @cfg {Boolean} todayHighlight default false
20746  * @cfg {Boolean} todayBtn default false
20747  * @cfg {Boolean} calendarWeeks default false
20748  * @cfg {Object} daysOfWeekDisabled default empty
20749  * @cfg {Boolean} singleMode default false (true | false)
20750  * 
20751  * @cfg {Boolean} keyboardNavigation default true
20752  * @cfg {String} language default en
20753  * 
20754  * @constructor
20755  * Create a new DateField
20756  * @param {Object} config The config object
20757  */
20758
20759 Roo.bootstrap.DateField = function(config){
20760     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20761      this.addEvents({
20762             /**
20763              * @event show
20764              * Fires when this field show.
20765              * @param {Roo.bootstrap.DateField} this
20766              * @param {Mixed} date The date value
20767              */
20768             show : true,
20769             /**
20770              * @event show
20771              * Fires when this field hide.
20772              * @param {Roo.bootstrap.DateField} this
20773              * @param {Mixed} date The date value
20774              */
20775             hide : true,
20776             /**
20777              * @event select
20778              * Fires when select a date.
20779              * @param {Roo.bootstrap.DateField} this
20780              * @param {Mixed} date The date value
20781              */
20782             select : true,
20783             /**
20784              * @event beforeselect
20785              * Fires when before select a date.
20786              * @param {Roo.bootstrap.DateField} this
20787              * @param {Mixed} date The date value
20788              */
20789             beforeselect : true
20790         });
20791 };
20792
20793 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20794     
20795     /**
20796      * @cfg {String} format
20797      * The default date format string which can be overriden for localization support.  The format must be
20798      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20799      */
20800     format : "m/d/y",
20801     /**
20802      * @cfg {String} altFormats
20803      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20804      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20805      */
20806     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20807     
20808     weekStart : 0,
20809     
20810     viewMode : '',
20811     
20812     minViewMode : '',
20813     
20814     todayHighlight : false,
20815     
20816     todayBtn: false,
20817     
20818     language: 'en',
20819     
20820     keyboardNavigation: true,
20821     
20822     calendarWeeks: false,
20823     
20824     startDate: -Infinity,
20825     
20826     endDate: Infinity,
20827     
20828     daysOfWeekDisabled: [],
20829     
20830     _events: [],
20831     
20832     singleMode : false,
20833     
20834     UTCDate: function()
20835     {
20836         return new Date(Date.UTC.apply(Date, arguments));
20837     },
20838     
20839     UTCToday: function()
20840     {
20841         var today = new Date();
20842         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20843     },
20844     
20845     getDate: function() {
20846             var d = this.getUTCDate();
20847             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20848     },
20849     
20850     getUTCDate: function() {
20851             return this.date;
20852     },
20853     
20854     setDate: function(d) {
20855             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20856     },
20857     
20858     setUTCDate: function(d) {
20859             this.date = d;
20860             this.setValue(this.formatDate(this.date));
20861     },
20862         
20863     onRender: function(ct, position)
20864     {
20865         
20866         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20867         
20868         this.language = this.language || 'en';
20869         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20870         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20871         
20872         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20873         this.format = this.format || 'm/d/y';
20874         this.isInline = false;
20875         this.isInput = true;
20876         this.component = this.el.select('.add-on', true).first() || false;
20877         this.component = (this.component && this.component.length === 0) ? false : this.component;
20878         this.hasInput = this.component && this.inputEl().length;
20879         
20880         if (typeof(this.minViewMode === 'string')) {
20881             switch (this.minViewMode) {
20882                 case 'months':
20883                     this.minViewMode = 1;
20884                     break;
20885                 case 'years':
20886                     this.minViewMode = 2;
20887                     break;
20888                 default:
20889                     this.minViewMode = 0;
20890                     break;
20891             }
20892         }
20893         
20894         if (typeof(this.viewMode === 'string')) {
20895             switch (this.viewMode) {
20896                 case 'months':
20897                     this.viewMode = 1;
20898                     break;
20899                 case 'years':
20900                     this.viewMode = 2;
20901                     break;
20902                 default:
20903                     this.viewMode = 0;
20904                     break;
20905             }
20906         }
20907                 
20908         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20909         
20910 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20911         
20912         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20913         
20914         this.picker().on('mousedown', this.onMousedown, this);
20915         this.picker().on('click', this.onClick, this);
20916         
20917         this.picker().addClass('datepicker-dropdown');
20918         
20919         this.startViewMode = this.viewMode;
20920         
20921         if(this.singleMode){
20922             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20923                 v.setVisibilityMode(Roo.Element.DISPLAY);
20924                 v.hide();
20925             });
20926             
20927             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20928                 v.setStyle('width', '189px');
20929             });
20930         }
20931         
20932         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20933             if(!this.calendarWeeks){
20934                 v.remove();
20935                 return;
20936             }
20937             
20938             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20939             v.attr('colspan', function(i, val){
20940                 return parseInt(val) + 1;
20941             });
20942         });
20943                         
20944         
20945         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20946         
20947         this.setStartDate(this.startDate);
20948         this.setEndDate(this.endDate);
20949         
20950         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20951         
20952         this.fillDow();
20953         this.fillMonths();
20954         this.update();
20955         this.showMode();
20956         
20957         if(this.isInline) {
20958             this.showPopup();
20959         }
20960     },
20961     
20962     picker : function()
20963     {
20964         return this.pickerEl;
20965 //        return this.el.select('.datepicker', true).first();
20966     },
20967     
20968     fillDow: function()
20969     {
20970         var dowCnt = this.weekStart;
20971         
20972         var dow = {
20973             tag: 'tr',
20974             cn: [
20975                 
20976             ]
20977         };
20978         
20979         if(this.calendarWeeks){
20980             dow.cn.push({
20981                 tag: 'th',
20982                 cls: 'cw',
20983                 html: '&nbsp;'
20984             })
20985         }
20986         
20987         while (dowCnt < this.weekStart + 7) {
20988             dow.cn.push({
20989                 tag: 'th',
20990                 cls: 'dow',
20991                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20992             });
20993         }
20994         
20995         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20996     },
20997     
20998     fillMonths: function()
20999     {    
21000         var i = 0;
21001         var months = this.picker().select('>.datepicker-months td', true).first();
21002         
21003         months.dom.innerHTML = '';
21004         
21005         while (i < 12) {
21006             var month = {
21007                 tag: 'span',
21008                 cls: 'month',
21009                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21010             };
21011             
21012             months.createChild(month);
21013         }
21014         
21015     },
21016     
21017     update: function()
21018     {
21019         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;
21020         
21021         if (this.date < this.startDate) {
21022             this.viewDate = new Date(this.startDate);
21023         } else if (this.date > this.endDate) {
21024             this.viewDate = new Date(this.endDate);
21025         } else {
21026             this.viewDate = new Date(this.date);
21027         }
21028         
21029         this.fill();
21030     },
21031     
21032     fill: function() 
21033     {
21034         var d = new Date(this.viewDate),
21035                 year = d.getUTCFullYear(),
21036                 month = d.getUTCMonth(),
21037                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21038                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21039                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21040                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21041                 currentDate = this.date && this.date.valueOf(),
21042                 today = this.UTCToday();
21043         
21044         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21045         
21046 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21047         
21048 //        this.picker.select('>tfoot th.today').
21049 //                                              .text(dates[this.language].today)
21050 //                                              .toggle(this.todayBtn !== false);
21051     
21052         this.updateNavArrows();
21053         this.fillMonths();
21054                                                 
21055         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21056         
21057         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21058          
21059         prevMonth.setUTCDate(day);
21060         
21061         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21062         
21063         var nextMonth = new Date(prevMonth);
21064         
21065         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21066         
21067         nextMonth = nextMonth.valueOf();
21068         
21069         var fillMonths = false;
21070         
21071         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21072         
21073         while(prevMonth.valueOf() <= nextMonth) {
21074             var clsName = '';
21075             
21076             if (prevMonth.getUTCDay() === this.weekStart) {
21077                 if(fillMonths){
21078                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21079                 }
21080                     
21081                 fillMonths = {
21082                     tag: 'tr',
21083                     cn: []
21084                 };
21085                 
21086                 if(this.calendarWeeks){
21087                     // ISO 8601: First week contains first thursday.
21088                     // ISO also states week starts on Monday, but we can be more abstract here.
21089                     var
21090                     // Start of current week: based on weekstart/current date
21091                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21092                     // Thursday of this week
21093                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21094                     // First Thursday of year, year from thursday
21095                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21096                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21097                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21098                     
21099                     fillMonths.cn.push({
21100                         tag: 'td',
21101                         cls: 'cw',
21102                         html: calWeek
21103                     });
21104                 }
21105             }
21106             
21107             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21108                 clsName += ' old';
21109             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21110                 clsName += ' new';
21111             }
21112             if (this.todayHighlight &&
21113                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21114                 prevMonth.getUTCMonth() == today.getMonth() &&
21115                 prevMonth.getUTCDate() == today.getDate()) {
21116                 clsName += ' today';
21117             }
21118             
21119             if (currentDate && prevMonth.valueOf() === currentDate) {
21120                 clsName += ' active';
21121             }
21122             
21123             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21124                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21125                     clsName += ' disabled';
21126             }
21127             
21128             fillMonths.cn.push({
21129                 tag: 'td',
21130                 cls: 'day ' + clsName,
21131                 html: prevMonth.getDate()
21132             });
21133             
21134             prevMonth.setDate(prevMonth.getDate()+1);
21135         }
21136           
21137         var currentYear = this.date && this.date.getUTCFullYear();
21138         var currentMonth = this.date && this.date.getUTCMonth();
21139         
21140         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21141         
21142         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21143             v.removeClass('active');
21144             
21145             if(currentYear === year && k === currentMonth){
21146                 v.addClass('active');
21147             }
21148             
21149             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21150                 v.addClass('disabled');
21151             }
21152             
21153         });
21154         
21155         
21156         year = parseInt(year/10, 10) * 10;
21157         
21158         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21159         
21160         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21161         
21162         year -= 1;
21163         for (var i = -1; i < 11; i++) {
21164             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21165                 tag: 'span',
21166                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21167                 html: year
21168             });
21169             
21170             year += 1;
21171         }
21172     },
21173     
21174     showMode: function(dir) 
21175     {
21176         if (dir) {
21177             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21178         }
21179         
21180         Roo.each(this.picker().select('>div',true).elements, function(v){
21181             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21182             v.hide();
21183         });
21184         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21185     },
21186     
21187     place: function()
21188     {
21189         if(this.isInline) {
21190             return;
21191         }
21192         
21193         this.picker().removeClass(['bottom', 'top']);
21194         
21195         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21196             /*
21197              * place to the top of element!
21198              *
21199              */
21200             
21201             this.picker().addClass('top');
21202             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21203             
21204             return;
21205         }
21206         
21207         this.picker().addClass('bottom');
21208         
21209         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21210     },
21211     
21212     parseDate : function(value)
21213     {
21214         if(!value || value instanceof Date){
21215             return value;
21216         }
21217         var v = Date.parseDate(value, this.format);
21218         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21219             v = Date.parseDate(value, 'Y-m-d');
21220         }
21221         if(!v && this.altFormats){
21222             if(!this.altFormatsArray){
21223                 this.altFormatsArray = this.altFormats.split("|");
21224             }
21225             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21226                 v = Date.parseDate(value, this.altFormatsArray[i]);
21227             }
21228         }
21229         return v;
21230     },
21231     
21232     formatDate : function(date, fmt)
21233     {   
21234         return (!date || !(date instanceof Date)) ?
21235         date : date.dateFormat(fmt || this.format);
21236     },
21237     
21238     onFocus : function()
21239     {
21240         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21241         this.showPopup();
21242     },
21243     
21244     onBlur : function()
21245     {
21246         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21247         
21248         var d = this.inputEl().getValue();
21249         
21250         this.setValue(d);
21251                 
21252         this.hidePopup();
21253     },
21254     
21255     showPopup : function()
21256     {
21257         this.picker().show();
21258         this.update();
21259         this.place();
21260         
21261         this.fireEvent('showpopup', this, this.date);
21262     },
21263     
21264     hidePopup : function()
21265     {
21266         if(this.isInline) {
21267             return;
21268         }
21269         this.picker().hide();
21270         this.viewMode = this.startViewMode;
21271         this.showMode();
21272         
21273         this.fireEvent('hidepopup', this, this.date);
21274         
21275     },
21276     
21277     onMousedown: function(e)
21278     {
21279         e.stopPropagation();
21280         e.preventDefault();
21281     },
21282     
21283     keyup: function(e)
21284     {
21285         Roo.bootstrap.DateField.superclass.keyup.call(this);
21286         this.update();
21287     },
21288
21289     setValue: function(v)
21290     {
21291         if(this.fireEvent('beforeselect', this, v) !== false){
21292             var d = new Date(this.parseDate(v) ).clearTime();
21293         
21294             if(isNaN(d.getTime())){
21295                 this.date = this.viewDate = '';
21296                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21297                 return;
21298             }
21299
21300             v = this.formatDate(d);
21301
21302             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21303
21304             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21305
21306             this.update();
21307
21308             this.fireEvent('select', this, this.date);
21309         }
21310     },
21311     
21312     getValue: function()
21313     {
21314         return this.formatDate(this.date);
21315     },
21316     
21317     fireKey: function(e)
21318     {
21319         if (!this.picker().isVisible()){
21320             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21321                 this.showPopup();
21322             }
21323             return;
21324         }
21325         
21326         var dateChanged = false,
21327         dir, day, month,
21328         newDate, newViewDate;
21329         
21330         switch(e.keyCode){
21331             case 27: // escape
21332                 this.hidePopup();
21333                 e.preventDefault();
21334                 break;
21335             case 37: // left
21336             case 39: // right
21337                 if (!this.keyboardNavigation) {
21338                     break;
21339                 }
21340                 dir = e.keyCode == 37 ? -1 : 1;
21341                 
21342                 if (e.ctrlKey){
21343                     newDate = this.moveYear(this.date, dir);
21344                     newViewDate = this.moveYear(this.viewDate, dir);
21345                 } else if (e.shiftKey){
21346                     newDate = this.moveMonth(this.date, dir);
21347                     newViewDate = this.moveMonth(this.viewDate, dir);
21348                 } else {
21349                     newDate = new Date(this.date);
21350                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21351                     newViewDate = new Date(this.viewDate);
21352                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21353                 }
21354                 if (this.dateWithinRange(newDate)){
21355                     this.date = newDate;
21356                     this.viewDate = newViewDate;
21357                     this.setValue(this.formatDate(this.date));
21358 //                    this.update();
21359                     e.preventDefault();
21360                     dateChanged = true;
21361                 }
21362                 break;
21363             case 38: // up
21364             case 40: // down
21365                 if (!this.keyboardNavigation) {
21366                     break;
21367                 }
21368                 dir = e.keyCode == 38 ? -1 : 1;
21369                 if (e.ctrlKey){
21370                     newDate = this.moveYear(this.date, dir);
21371                     newViewDate = this.moveYear(this.viewDate, dir);
21372                 } else if (e.shiftKey){
21373                     newDate = this.moveMonth(this.date, dir);
21374                     newViewDate = this.moveMonth(this.viewDate, dir);
21375                 } else {
21376                     newDate = new Date(this.date);
21377                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21378                     newViewDate = new Date(this.viewDate);
21379                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21380                 }
21381                 if (this.dateWithinRange(newDate)){
21382                     this.date = newDate;
21383                     this.viewDate = newViewDate;
21384                     this.setValue(this.formatDate(this.date));
21385 //                    this.update();
21386                     e.preventDefault();
21387                     dateChanged = true;
21388                 }
21389                 break;
21390             case 13: // enter
21391                 this.setValue(this.formatDate(this.date));
21392                 this.hidePopup();
21393                 e.preventDefault();
21394                 break;
21395             case 9: // tab
21396                 this.setValue(this.formatDate(this.date));
21397                 this.hidePopup();
21398                 break;
21399             case 16: // shift
21400             case 17: // ctrl
21401             case 18: // alt
21402                 break;
21403             default :
21404                 this.hidePopup();
21405                 
21406         }
21407     },
21408     
21409     
21410     onClick: function(e) 
21411     {
21412         e.stopPropagation();
21413         e.preventDefault();
21414         
21415         var target = e.getTarget();
21416         
21417         if(target.nodeName.toLowerCase() === 'i'){
21418             target = Roo.get(target).dom.parentNode;
21419         }
21420         
21421         var nodeName = target.nodeName;
21422         var className = target.className;
21423         var html = target.innerHTML;
21424         //Roo.log(nodeName);
21425         
21426         switch(nodeName.toLowerCase()) {
21427             case 'th':
21428                 switch(className) {
21429                     case 'switch':
21430                         this.showMode(1);
21431                         break;
21432                     case 'prev':
21433                     case 'next':
21434                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21435                         switch(this.viewMode){
21436                                 case 0:
21437                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21438                                         break;
21439                                 case 1:
21440                                 case 2:
21441                                         this.viewDate = this.moveYear(this.viewDate, dir);
21442                                         break;
21443                         }
21444                         this.fill();
21445                         break;
21446                     case 'today':
21447                         var date = new Date();
21448                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21449 //                        this.fill()
21450                         this.setValue(this.formatDate(this.date));
21451                         
21452                         this.hidePopup();
21453                         break;
21454                 }
21455                 break;
21456             case 'span':
21457                 if (className.indexOf('disabled') < 0) {
21458                     this.viewDate.setUTCDate(1);
21459                     if (className.indexOf('month') > -1) {
21460                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21461                     } else {
21462                         var year = parseInt(html, 10) || 0;
21463                         this.viewDate.setUTCFullYear(year);
21464                         
21465                     }
21466                     
21467                     if(this.singleMode){
21468                         this.setValue(this.formatDate(this.viewDate));
21469                         this.hidePopup();
21470                         return;
21471                     }
21472                     
21473                     this.showMode(-1);
21474                     this.fill();
21475                 }
21476                 break;
21477                 
21478             case 'td':
21479                 //Roo.log(className);
21480                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21481                     var day = parseInt(html, 10) || 1;
21482                     var year = this.viewDate.getUTCFullYear(),
21483                         month = this.viewDate.getUTCMonth();
21484
21485                     if (className.indexOf('old') > -1) {
21486                         if(month === 0 ){
21487                             month = 11;
21488                             year -= 1;
21489                         }else{
21490                             month -= 1;
21491                         }
21492                     } else if (className.indexOf('new') > -1) {
21493                         if (month == 11) {
21494                             month = 0;
21495                             year += 1;
21496                         } else {
21497                             month += 1;
21498                         }
21499                     }
21500                     //Roo.log([year,month,day]);
21501                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21502                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21503 //                    this.fill();
21504                     //Roo.log(this.formatDate(this.date));
21505                     this.setValue(this.formatDate(this.date));
21506                     this.hidePopup();
21507                 }
21508                 break;
21509         }
21510     },
21511     
21512     setStartDate: function(startDate)
21513     {
21514         this.startDate = startDate || -Infinity;
21515         if (this.startDate !== -Infinity) {
21516             this.startDate = this.parseDate(this.startDate);
21517         }
21518         this.update();
21519         this.updateNavArrows();
21520     },
21521
21522     setEndDate: function(endDate)
21523     {
21524         this.endDate = endDate || Infinity;
21525         if (this.endDate !== Infinity) {
21526             this.endDate = this.parseDate(this.endDate);
21527         }
21528         this.update();
21529         this.updateNavArrows();
21530     },
21531     
21532     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21533     {
21534         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21535         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21536             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21537         }
21538         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21539             return parseInt(d, 10);
21540         });
21541         this.update();
21542         this.updateNavArrows();
21543     },
21544     
21545     updateNavArrows: function() 
21546     {
21547         if(this.singleMode){
21548             return;
21549         }
21550         
21551         var d = new Date(this.viewDate),
21552         year = d.getUTCFullYear(),
21553         month = d.getUTCMonth();
21554         
21555         Roo.each(this.picker().select('.prev', true).elements, function(v){
21556             v.show();
21557             switch (this.viewMode) {
21558                 case 0:
21559
21560                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21561                         v.hide();
21562                     }
21563                     break;
21564                 case 1:
21565                 case 2:
21566                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21567                         v.hide();
21568                     }
21569                     break;
21570             }
21571         });
21572         
21573         Roo.each(this.picker().select('.next', true).elements, function(v){
21574             v.show();
21575             switch (this.viewMode) {
21576                 case 0:
21577
21578                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21579                         v.hide();
21580                     }
21581                     break;
21582                 case 1:
21583                 case 2:
21584                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21585                         v.hide();
21586                     }
21587                     break;
21588             }
21589         })
21590     },
21591     
21592     moveMonth: function(date, dir)
21593     {
21594         if (!dir) {
21595             return date;
21596         }
21597         var new_date = new Date(date.valueOf()),
21598         day = new_date.getUTCDate(),
21599         month = new_date.getUTCMonth(),
21600         mag = Math.abs(dir),
21601         new_month, test;
21602         dir = dir > 0 ? 1 : -1;
21603         if (mag == 1){
21604             test = dir == -1
21605             // If going back one month, make sure month is not current month
21606             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21607             ? function(){
21608                 return new_date.getUTCMonth() == month;
21609             }
21610             // If going forward one month, make sure month is as expected
21611             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21612             : function(){
21613                 return new_date.getUTCMonth() != new_month;
21614             };
21615             new_month = month + dir;
21616             new_date.setUTCMonth(new_month);
21617             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21618             if (new_month < 0 || new_month > 11) {
21619                 new_month = (new_month + 12) % 12;
21620             }
21621         } else {
21622             // For magnitudes >1, move one month at a time...
21623             for (var i=0; i<mag; i++) {
21624                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21625                 new_date = this.moveMonth(new_date, dir);
21626             }
21627             // ...then reset the day, keeping it in the new month
21628             new_month = new_date.getUTCMonth();
21629             new_date.setUTCDate(day);
21630             test = function(){
21631                 return new_month != new_date.getUTCMonth();
21632             };
21633         }
21634         // Common date-resetting loop -- if date is beyond end of month, make it
21635         // end of month
21636         while (test()){
21637             new_date.setUTCDate(--day);
21638             new_date.setUTCMonth(new_month);
21639         }
21640         return new_date;
21641     },
21642
21643     moveYear: function(date, dir)
21644     {
21645         return this.moveMonth(date, dir*12);
21646     },
21647
21648     dateWithinRange: function(date)
21649     {
21650         return date >= this.startDate && date <= this.endDate;
21651     },
21652
21653     
21654     remove: function() 
21655     {
21656         this.picker().remove();
21657     },
21658     
21659     validateValue : function(value)
21660     {
21661         if(this.getVisibilityEl().hasClass('hidden')){
21662             return true;
21663         }
21664         
21665         if(value.length < 1)  {
21666             if(this.allowBlank){
21667                 return true;
21668             }
21669             return false;
21670         }
21671         
21672         if(value.length < this.minLength){
21673             return false;
21674         }
21675         if(value.length > this.maxLength){
21676             return false;
21677         }
21678         if(this.vtype){
21679             var vt = Roo.form.VTypes;
21680             if(!vt[this.vtype](value, this)){
21681                 return false;
21682             }
21683         }
21684         if(typeof this.validator == "function"){
21685             var msg = this.validator(value);
21686             if(msg !== true){
21687                 return false;
21688             }
21689         }
21690         
21691         if(this.regex && !this.regex.test(value)){
21692             return false;
21693         }
21694         
21695         if(typeof(this.parseDate(value)) == 'undefined'){
21696             return false;
21697         }
21698         
21699         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21700             return false;
21701         }      
21702         
21703         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21704             return false;
21705         } 
21706         
21707         
21708         return true;
21709     },
21710     
21711     reset : function()
21712     {
21713         this.date = this.viewDate = '';
21714         
21715         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21716     }
21717    
21718 });
21719
21720 Roo.apply(Roo.bootstrap.DateField,  {
21721     
21722     head : {
21723         tag: 'thead',
21724         cn: [
21725         {
21726             tag: 'tr',
21727             cn: [
21728             {
21729                 tag: 'th',
21730                 cls: 'prev',
21731                 html: '<i class="fa fa-arrow-left"/>'
21732             },
21733             {
21734                 tag: 'th',
21735                 cls: 'switch',
21736                 colspan: '5'
21737             },
21738             {
21739                 tag: 'th',
21740                 cls: 'next',
21741                 html: '<i class="fa fa-arrow-right"/>'
21742             }
21743
21744             ]
21745         }
21746         ]
21747     },
21748     
21749     content : {
21750         tag: 'tbody',
21751         cn: [
21752         {
21753             tag: 'tr',
21754             cn: [
21755             {
21756                 tag: 'td',
21757                 colspan: '7'
21758             }
21759             ]
21760         }
21761         ]
21762     },
21763     
21764     footer : {
21765         tag: 'tfoot',
21766         cn: [
21767         {
21768             tag: 'tr',
21769             cn: [
21770             {
21771                 tag: 'th',
21772                 colspan: '7',
21773                 cls: 'today'
21774             }
21775                     
21776             ]
21777         }
21778         ]
21779     },
21780     
21781     dates:{
21782         en: {
21783             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21784             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21785             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21786             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21787             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21788             today: "Today"
21789         }
21790     },
21791     
21792     modes: [
21793     {
21794         clsName: 'days',
21795         navFnc: 'Month',
21796         navStep: 1
21797     },
21798     {
21799         clsName: 'months',
21800         navFnc: 'FullYear',
21801         navStep: 1
21802     },
21803     {
21804         clsName: 'years',
21805         navFnc: 'FullYear',
21806         navStep: 10
21807     }]
21808 });
21809
21810 Roo.apply(Roo.bootstrap.DateField,  {
21811   
21812     template : {
21813         tag: 'div',
21814         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21815         cn: [
21816         {
21817             tag: 'div',
21818             cls: 'datepicker-days',
21819             cn: [
21820             {
21821                 tag: 'table',
21822                 cls: 'table-condensed',
21823                 cn:[
21824                 Roo.bootstrap.DateField.head,
21825                 {
21826                     tag: 'tbody'
21827                 },
21828                 Roo.bootstrap.DateField.footer
21829                 ]
21830             }
21831             ]
21832         },
21833         {
21834             tag: 'div',
21835             cls: 'datepicker-months',
21836             cn: [
21837             {
21838                 tag: 'table',
21839                 cls: 'table-condensed',
21840                 cn:[
21841                 Roo.bootstrap.DateField.head,
21842                 Roo.bootstrap.DateField.content,
21843                 Roo.bootstrap.DateField.footer
21844                 ]
21845             }
21846             ]
21847         },
21848         {
21849             tag: 'div',
21850             cls: 'datepicker-years',
21851             cn: [
21852             {
21853                 tag: 'table',
21854                 cls: 'table-condensed',
21855                 cn:[
21856                 Roo.bootstrap.DateField.head,
21857                 Roo.bootstrap.DateField.content,
21858                 Roo.bootstrap.DateField.footer
21859                 ]
21860             }
21861             ]
21862         }
21863         ]
21864     }
21865 });
21866
21867  
21868
21869  /*
21870  * - LGPL
21871  *
21872  * TimeField
21873  * 
21874  */
21875
21876 /**
21877  * @class Roo.bootstrap.TimeField
21878  * @extends Roo.bootstrap.Input
21879  * Bootstrap DateField class
21880  * 
21881  * 
21882  * @constructor
21883  * Create a new TimeField
21884  * @param {Object} config The config object
21885  */
21886
21887 Roo.bootstrap.TimeField = function(config){
21888     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21889     this.addEvents({
21890             /**
21891              * @event show
21892              * Fires when this field show.
21893              * @param {Roo.bootstrap.DateField} thisthis
21894              * @param {Mixed} date The date value
21895              */
21896             show : true,
21897             /**
21898              * @event show
21899              * Fires when this field hide.
21900              * @param {Roo.bootstrap.DateField} this
21901              * @param {Mixed} date The date value
21902              */
21903             hide : true,
21904             /**
21905              * @event select
21906              * Fires when select a date.
21907              * @param {Roo.bootstrap.DateField} this
21908              * @param {Mixed} date The date value
21909              */
21910             select : true
21911         });
21912 };
21913
21914 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21915     
21916     /**
21917      * @cfg {String} format
21918      * The default time format string which can be overriden for localization support.  The format must be
21919      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21920      */
21921     format : "H:i",
21922        
21923     onRender: function(ct, position)
21924     {
21925         
21926         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21927                 
21928         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21929         
21930         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21931         
21932         this.pop = this.picker().select('>.datepicker-time',true).first();
21933         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21934         
21935         this.picker().on('mousedown', this.onMousedown, this);
21936         this.picker().on('click', this.onClick, this);
21937         
21938         this.picker().addClass('datepicker-dropdown');
21939     
21940         this.fillTime();
21941         this.update();
21942             
21943         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21944         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21945         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21946         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21947         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21948         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21949
21950     },
21951     
21952     fireKey: function(e){
21953         if (!this.picker().isVisible()){
21954             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21955                 this.show();
21956             }
21957             return;
21958         }
21959
21960         e.preventDefault();
21961         
21962         switch(e.keyCode){
21963             case 27: // escape
21964                 this.hide();
21965                 break;
21966             case 37: // left
21967             case 39: // right
21968                 this.onTogglePeriod();
21969                 break;
21970             case 38: // up
21971                 this.onIncrementMinutes();
21972                 break;
21973             case 40: // down
21974                 this.onDecrementMinutes();
21975                 break;
21976             case 13: // enter
21977             case 9: // tab
21978                 this.setTime();
21979                 break;
21980         }
21981     },
21982     
21983     onClick: function(e) {
21984         e.stopPropagation();
21985         e.preventDefault();
21986     },
21987     
21988     picker : function()
21989     {
21990         return this.el.select('.datepicker', true).first();
21991     },
21992     
21993     fillTime: function()
21994     {    
21995         var time = this.pop.select('tbody', true).first();
21996         
21997         time.dom.innerHTML = '';
21998         
21999         time.createChild({
22000             tag: 'tr',
22001             cn: [
22002                 {
22003                     tag: 'td',
22004                     cn: [
22005                         {
22006                             tag: 'a',
22007                             href: '#',
22008                             cls: 'btn',
22009                             cn: [
22010                                 {
22011                                     tag: 'span',
22012                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22013                                 }
22014                             ]
22015                         } 
22016                     ]
22017                 },
22018                 {
22019                     tag: 'td',
22020                     cls: 'separator'
22021                 },
22022                 {
22023                     tag: 'td',
22024                     cn: [
22025                         {
22026                             tag: 'a',
22027                             href: '#',
22028                             cls: 'btn',
22029                             cn: [
22030                                 {
22031                                     tag: 'span',
22032                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22033                                 }
22034                             ]
22035                         }
22036                     ]
22037                 },
22038                 {
22039                     tag: 'td',
22040                     cls: 'separator'
22041                 }
22042             ]
22043         });
22044         
22045         time.createChild({
22046             tag: 'tr',
22047             cn: [
22048                 {
22049                     tag: 'td',
22050                     cn: [
22051                         {
22052                             tag: 'span',
22053                             cls: 'timepicker-hour',
22054                             html: '00'
22055                         }  
22056                     ]
22057                 },
22058                 {
22059                     tag: 'td',
22060                     cls: 'separator',
22061                     html: ':'
22062                 },
22063                 {
22064                     tag: 'td',
22065                     cn: [
22066                         {
22067                             tag: 'span',
22068                             cls: 'timepicker-minute',
22069                             html: '00'
22070                         }  
22071                     ]
22072                 },
22073                 {
22074                     tag: 'td',
22075                     cls: 'separator'
22076                 },
22077                 {
22078                     tag: 'td',
22079                     cn: [
22080                         {
22081                             tag: 'button',
22082                             type: 'button',
22083                             cls: 'btn btn-primary period',
22084                             html: 'AM'
22085                             
22086                         }
22087                     ]
22088                 }
22089             ]
22090         });
22091         
22092         time.createChild({
22093             tag: 'tr',
22094             cn: [
22095                 {
22096                     tag: 'td',
22097                     cn: [
22098                         {
22099                             tag: 'a',
22100                             href: '#',
22101                             cls: 'btn',
22102                             cn: [
22103                                 {
22104                                     tag: 'span',
22105                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22106                                 }
22107                             ]
22108                         }
22109                     ]
22110                 },
22111                 {
22112                     tag: 'td',
22113                     cls: 'separator'
22114                 },
22115                 {
22116                     tag: 'td',
22117                     cn: [
22118                         {
22119                             tag: 'a',
22120                             href: '#',
22121                             cls: 'btn',
22122                             cn: [
22123                                 {
22124                                     tag: 'span',
22125                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22126                                 }
22127                             ]
22128                         }
22129                     ]
22130                 },
22131                 {
22132                     tag: 'td',
22133                     cls: 'separator'
22134                 }
22135             ]
22136         });
22137         
22138     },
22139     
22140     update: function()
22141     {
22142         
22143         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22144         
22145         this.fill();
22146     },
22147     
22148     fill: function() 
22149     {
22150         var hours = this.time.getHours();
22151         var minutes = this.time.getMinutes();
22152         var period = 'AM';
22153         
22154         if(hours > 11){
22155             period = 'PM';
22156         }
22157         
22158         if(hours == 0){
22159             hours = 12;
22160         }
22161         
22162         
22163         if(hours > 12){
22164             hours = hours - 12;
22165         }
22166         
22167         if(hours < 10){
22168             hours = '0' + hours;
22169         }
22170         
22171         if(minutes < 10){
22172             minutes = '0' + minutes;
22173         }
22174         
22175         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22176         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22177         this.pop.select('button', true).first().dom.innerHTML = period;
22178         
22179     },
22180     
22181     place: function()
22182     {   
22183         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22184         
22185         var cls = ['bottom'];
22186         
22187         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22188             cls.pop();
22189             cls.push('top');
22190         }
22191         
22192         cls.push('right');
22193         
22194         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22195             cls.pop();
22196             cls.push('left');
22197         }
22198         
22199         this.picker().addClass(cls.join('-'));
22200         
22201         var _this = this;
22202         
22203         Roo.each(cls, function(c){
22204             if(c == 'bottom'){
22205                 _this.picker().setTop(_this.inputEl().getHeight());
22206                 return;
22207             }
22208             if(c == 'top'){
22209                 _this.picker().setTop(0 - _this.picker().getHeight());
22210                 return;
22211             }
22212             
22213             if(c == 'left'){
22214                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22215                 return;
22216             }
22217             if(c == 'right'){
22218                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22219                 return;
22220             }
22221         });
22222         
22223     },
22224   
22225     onFocus : function()
22226     {
22227         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22228         this.show();
22229     },
22230     
22231     onBlur : function()
22232     {
22233         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22234         this.hide();
22235     },
22236     
22237     show : function()
22238     {
22239         this.picker().show();
22240         this.pop.show();
22241         this.update();
22242         this.place();
22243         
22244         this.fireEvent('show', this, this.date);
22245     },
22246     
22247     hide : function()
22248     {
22249         this.picker().hide();
22250         this.pop.hide();
22251         
22252         this.fireEvent('hide', this, this.date);
22253     },
22254     
22255     setTime : function()
22256     {
22257         this.hide();
22258         this.setValue(this.time.format(this.format));
22259         
22260         this.fireEvent('select', this, this.date);
22261         
22262         
22263     },
22264     
22265     onMousedown: function(e){
22266         e.stopPropagation();
22267         e.preventDefault();
22268     },
22269     
22270     onIncrementHours: function()
22271     {
22272         Roo.log('onIncrementHours');
22273         this.time = this.time.add(Date.HOUR, 1);
22274         this.update();
22275         
22276     },
22277     
22278     onDecrementHours: function()
22279     {
22280         Roo.log('onDecrementHours');
22281         this.time = this.time.add(Date.HOUR, -1);
22282         this.update();
22283     },
22284     
22285     onIncrementMinutes: function()
22286     {
22287         Roo.log('onIncrementMinutes');
22288         this.time = this.time.add(Date.MINUTE, 1);
22289         this.update();
22290     },
22291     
22292     onDecrementMinutes: function()
22293     {
22294         Roo.log('onDecrementMinutes');
22295         this.time = this.time.add(Date.MINUTE, -1);
22296         this.update();
22297     },
22298     
22299     onTogglePeriod: function()
22300     {
22301         Roo.log('onTogglePeriod');
22302         this.time = this.time.add(Date.HOUR, 12);
22303         this.update();
22304     }
22305     
22306    
22307 });
22308
22309 Roo.apply(Roo.bootstrap.TimeField,  {
22310     
22311     content : {
22312         tag: 'tbody',
22313         cn: [
22314             {
22315                 tag: 'tr',
22316                 cn: [
22317                 {
22318                     tag: 'td',
22319                     colspan: '7'
22320                 }
22321                 ]
22322             }
22323         ]
22324     },
22325     
22326     footer : {
22327         tag: 'tfoot',
22328         cn: [
22329             {
22330                 tag: 'tr',
22331                 cn: [
22332                 {
22333                     tag: 'th',
22334                     colspan: '7',
22335                     cls: '',
22336                     cn: [
22337                         {
22338                             tag: 'button',
22339                             cls: 'btn btn-info ok',
22340                             html: 'OK'
22341                         }
22342                     ]
22343                 }
22344
22345                 ]
22346             }
22347         ]
22348     }
22349 });
22350
22351 Roo.apply(Roo.bootstrap.TimeField,  {
22352   
22353     template : {
22354         tag: 'div',
22355         cls: 'datepicker dropdown-menu',
22356         cn: [
22357             {
22358                 tag: 'div',
22359                 cls: 'datepicker-time',
22360                 cn: [
22361                 {
22362                     tag: 'table',
22363                     cls: 'table-condensed',
22364                     cn:[
22365                     Roo.bootstrap.TimeField.content,
22366                     Roo.bootstrap.TimeField.footer
22367                     ]
22368                 }
22369                 ]
22370             }
22371         ]
22372     }
22373 });
22374
22375  
22376
22377  /*
22378  * - LGPL
22379  *
22380  * MonthField
22381  * 
22382  */
22383
22384 /**
22385  * @class Roo.bootstrap.MonthField
22386  * @extends Roo.bootstrap.Input
22387  * Bootstrap MonthField class
22388  * 
22389  * @cfg {String} language default en
22390  * 
22391  * @constructor
22392  * Create a new MonthField
22393  * @param {Object} config The config object
22394  */
22395
22396 Roo.bootstrap.MonthField = function(config){
22397     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22398     
22399     this.addEvents({
22400         /**
22401          * @event show
22402          * Fires when this field show.
22403          * @param {Roo.bootstrap.MonthField} this
22404          * @param {Mixed} date The date value
22405          */
22406         show : true,
22407         /**
22408          * @event show
22409          * Fires when this field hide.
22410          * @param {Roo.bootstrap.MonthField} this
22411          * @param {Mixed} date The date value
22412          */
22413         hide : true,
22414         /**
22415          * @event select
22416          * Fires when select a date.
22417          * @param {Roo.bootstrap.MonthField} this
22418          * @param {String} oldvalue The old value
22419          * @param {String} newvalue The new value
22420          */
22421         select : true
22422     });
22423 };
22424
22425 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22426     
22427     onRender: function(ct, position)
22428     {
22429         
22430         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22431         
22432         this.language = this.language || 'en';
22433         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22434         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22435         
22436         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22437         this.isInline = false;
22438         this.isInput = true;
22439         this.component = this.el.select('.add-on', true).first() || false;
22440         this.component = (this.component && this.component.length === 0) ? false : this.component;
22441         this.hasInput = this.component && this.inputEL().length;
22442         
22443         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22444         
22445         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22446         
22447         this.picker().on('mousedown', this.onMousedown, this);
22448         this.picker().on('click', this.onClick, this);
22449         
22450         this.picker().addClass('datepicker-dropdown');
22451         
22452         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22453             v.setStyle('width', '189px');
22454         });
22455         
22456         this.fillMonths();
22457         
22458         this.update();
22459         
22460         if(this.isInline) {
22461             this.show();
22462         }
22463         
22464     },
22465     
22466     setValue: function(v, suppressEvent)
22467     {   
22468         var o = this.getValue();
22469         
22470         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22471         
22472         this.update();
22473
22474         if(suppressEvent !== true){
22475             this.fireEvent('select', this, o, v);
22476         }
22477         
22478     },
22479     
22480     getValue: function()
22481     {
22482         return this.value;
22483     },
22484     
22485     onClick: function(e) 
22486     {
22487         e.stopPropagation();
22488         e.preventDefault();
22489         
22490         var target = e.getTarget();
22491         
22492         if(target.nodeName.toLowerCase() === 'i'){
22493             target = Roo.get(target).dom.parentNode;
22494         }
22495         
22496         var nodeName = target.nodeName;
22497         var className = target.className;
22498         var html = target.innerHTML;
22499         
22500         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22501             return;
22502         }
22503         
22504         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22505         
22506         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22507         
22508         this.hide();
22509                         
22510     },
22511     
22512     picker : function()
22513     {
22514         return this.pickerEl;
22515     },
22516     
22517     fillMonths: function()
22518     {    
22519         var i = 0;
22520         var months = this.picker().select('>.datepicker-months td', true).first();
22521         
22522         months.dom.innerHTML = '';
22523         
22524         while (i < 12) {
22525             var month = {
22526                 tag: 'span',
22527                 cls: 'month',
22528                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22529             };
22530             
22531             months.createChild(month);
22532         }
22533         
22534     },
22535     
22536     update: function()
22537     {
22538         var _this = this;
22539         
22540         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22541             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22542         }
22543         
22544         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22545             e.removeClass('active');
22546             
22547             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22548                 e.addClass('active');
22549             }
22550         })
22551     },
22552     
22553     place: function()
22554     {
22555         if(this.isInline) {
22556             return;
22557         }
22558         
22559         this.picker().removeClass(['bottom', 'top']);
22560         
22561         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22562             /*
22563              * place to the top of element!
22564              *
22565              */
22566             
22567             this.picker().addClass('top');
22568             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22569             
22570             return;
22571         }
22572         
22573         this.picker().addClass('bottom');
22574         
22575         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22576     },
22577     
22578     onFocus : function()
22579     {
22580         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22581         this.show();
22582     },
22583     
22584     onBlur : function()
22585     {
22586         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22587         
22588         var d = this.inputEl().getValue();
22589         
22590         this.setValue(d);
22591                 
22592         this.hide();
22593     },
22594     
22595     show : function()
22596     {
22597         this.picker().show();
22598         this.picker().select('>.datepicker-months', true).first().show();
22599         this.update();
22600         this.place();
22601         
22602         this.fireEvent('show', this, this.date);
22603     },
22604     
22605     hide : function()
22606     {
22607         if(this.isInline) {
22608             return;
22609         }
22610         this.picker().hide();
22611         this.fireEvent('hide', this, this.date);
22612         
22613     },
22614     
22615     onMousedown: function(e)
22616     {
22617         e.stopPropagation();
22618         e.preventDefault();
22619     },
22620     
22621     keyup: function(e)
22622     {
22623         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22624         this.update();
22625     },
22626
22627     fireKey: function(e)
22628     {
22629         if (!this.picker().isVisible()){
22630             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22631                 this.show();
22632             }
22633             return;
22634         }
22635         
22636         var dir;
22637         
22638         switch(e.keyCode){
22639             case 27: // escape
22640                 this.hide();
22641                 e.preventDefault();
22642                 break;
22643             case 37: // left
22644             case 39: // right
22645                 dir = e.keyCode == 37 ? -1 : 1;
22646                 
22647                 this.vIndex = this.vIndex + dir;
22648                 
22649                 if(this.vIndex < 0){
22650                     this.vIndex = 0;
22651                 }
22652                 
22653                 if(this.vIndex > 11){
22654                     this.vIndex = 11;
22655                 }
22656                 
22657                 if(isNaN(this.vIndex)){
22658                     this.vIndex = 0;
22659                 }
22660                 
22661                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22662                 
22663                 break;
22664             case 38: // up
22665             case 40: // down
22666                 
22667                 dir = e.keyCode == 38 ? -1 : 1;
22668                 
22669                 this.vIndex = this.vIndex + dir * 4;
22670                 
22671                 if(this.vIndex < 0){
22672                     this.vIndex = 0;
22673                 }
22674                 
22675                 if(this.vIndex > 11){
22676                     this.vIndex = 11;
22677                 }
22678                 
22679                 if(isNaN(this.vIndex)){
22680                     this.vIndex = 0;
22681                 }
22682                 
22683                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22684                 break;
22685                 
22686             case 13: // enter
22687                 
22688                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22689                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22690                 }
22691                 
22692                 this.hide();
22693                 e.preventDefault();
22694                 break;
22695             case 9: // tab
22696                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22697                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22698                 }
22699                 this.hide();
22700                 break;
22701             case 16: // shift
22702             case 17: // ctrl
22703             case 18: // alt
22704                 break;
22705             default :
22706                 this.hide();
22707                 
22708         }
22709     },
22710     
22711     remove: function() 
22712     {
22713         this.picker().remove();
22714     }
22715    
22716 });
22717
22718 Roo.apply(Roo.bootstrap.MonthField,  {
22719     
22720     content : {
22721         tag: 'tbody',
22722         cn: [
22723         {
22724             tag: 'tr',
22725             cn: [
22726             {
22727                 tag: 'td',
22728                 colspan: '7'
22729             }
22730             ]
22731         }
22732         ]
22733     },
22734     
22735     dates:{
22736         en: {
22737             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22738             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22739         }
22740     }
22741 });
22742
22743 Roo.apply(Roo.bootstrap.MonthField,  {
22744   
22745     template : {
22746         tag: 'div',
22747         cls: 'datepicker dropdown-menu roo-dynamic',
22748         cn: [
22749             {
22750                 tag: 'div',
22751                 cls: 'datepicker-months',
22752                 cn: [
22753                 {
22754                     tag: 'table',
22755                     cls: 'table-condensed',
22756                     cn:[
22757                         Roo.bootstrap.DateField.content
22758                     ]
22759                 }
22760                 ]
22761             }
22762         ]
22763     }
22764 });
22765
22766  
22767
22768  
22769  /*
22770  * - LGPL
22771  *
22772  * CheckBox
22773  * 
22774  */
22775
22776 /**
22777  * @class Roo.bootstrap.CheckBox
22778  * @extends Roo.bootstrap.Input
22779  * Bootstrap CheckBox class
22780  * 
22781  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22782  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22783  * @cfg {String} boxLabel The text that appears beside the checkbox
22784  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22785  * @cfg {Boolean} checked initnal the element
22786  * @cfg {Boolean} inline inline the element (default false)
22787  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22788  * @cfg {String} tooltip label tooltip
22789  * 
22790  * @constructor
22791  * Create a new CheckBox
22792  * @param {Object} config The config object
22793  */
22794
22795 Roo.bootstrap.CheckBox = function(config){
22796     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22797    
22798     this.addEvents({
22799         /**
22800         * @event check
22801         * Fires when the element is checked or unchecked.
22802         * @param {Roo.bootstrap.CheckBox} this This input
22803         * @param {Boolean} checked The new checked value
22804         */
22805        check : true,
22806        /**
22807         * @event click
22808         * Fires when the element is click.
22809         * @param {Roo.bootstrap.CheckBox} this This input
22810         */
22811        click : true
22812     });
22813     
22814 };
22815
22816 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22817   
22818     inputType: 'checkbox',
22819     inputValue: 1,
22820     valueOff: 0,
22821     boxLabel: false,
22822     checked: false,
22823     weight : false,
22824     inline: false,
22825     tooltip : '',
22826     
22827     // checkbox success does not make any sense really.. 
22828     invalidClass : "",
22829     validClass : "",
22830     
22831     
22832     getAutoCreate : function()
22833     {
22834         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22835         
22836         var id = Roo.id();
22837         
22838         var cfg = {};
22839         
22840         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22841         
22842         if(this.inline){
22843             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22844         }
22845         
22846         var input =  {
22847             tag: 'input',
22848             id : id,
22849             type : this.inputType,
22850             value : this.inputValue,
22851             cls : 'roo-' + this.inputType, //'form-box',
22852             placeholder : this.placeholder || ''
22853             
22854         };
22855         
22856         if(this.inputType != 'radio'){
22857             var hidden =  {
22858                 tag: 'input',
22859                 type : 'hidden',
22860                 cls : 'roo-hidden-value',
22861                 value : this.checked ? this.inputValue : this.valueOff
22862             };
22863         }
22864         
22865             
22866         if (this.weight) { // Validity check?
22867             cfg.cls += " " + this.inputType + "-" + this.weight;
22868         }
22869         
22870         if (this.disabled) {
22871             input.disabled=true;
22872         }
22873         
22874         if(this.checked){
22875             input.checked = this.checked;
22876         }
22877         
22878         if (this.name) {
22879             
22880             input.name = this.name;
22881             
22882             if(this.inputType != 'radio'){
22883                 hidden.name = this.name;
22884                 input.name = '_hidden_' + this.name;
22885             }
22886         }
22887         
22888         if (this.size) {
22889             input.cls += ' input-' + this.size;
22890         }
22891         
22892         var settings=this;
22893         
22894         ['xs','sm','md','lg'].map(function(size){
22895             if (settings[size]) {
22896                 cfg.cls += ' col-' + size + '-' + settings[size];
22897             }
22898         });
22899         
22900         var inputblock = input;
22901          
22902         if (this.before || this.after) {
22903             
22904             inputblock = {
22905                 cls : 'input-group',
22906                 cn :  [] 
22907             };
22908             
22909             if (this.before) {
22910                 inputblock.cn.push({
22911                     tag :'span',
22912                     cls : 'input-group-addon',
22913                     html : this.before
22914                 });
22915             }
22916             
22917             inputblock.cn.push(input);
22918             
22919             if(this.inputType != 'radio'){
22920                 inputblock.cn.push(hidden);
22921             }
22922             
22923             if (this.after) {
22924                 inputblock.cn.push({
22925                     tag :'span',
22926                     cls : 'input-group-addon',
22927                     html : this.after
22928                 });
22929             }
22930             
22931         }
22932         var boxLabelCfg = false;
22933         
22934         if(this.boxLabel){
22935            
22936             boxLabelCfg = {
22937                 tag: 'label',
22938                 //'for': id, // box label is handled by onclick - so no for...
22939                 cls: 'box-label',
22940                 html: this.boxLabel
22941             };
22942             if(this.tooltip){
22943                 boxLabelCfg.tooltip = this.tooltip;
22944             }
22945              
22946         }
22947         
22948         
22949         if (align ==='left' && this.fieldLabel.length) {
22950 //                Roo.log("left and has label");
22951             cfg.cn = [
22952                 {
22953                     tag: 'label',
22954                     'for' :  id,
22955                     cls : 'control-label',
22956                     html : this.fieldLabel
22957                 },
22958                 {
22959                     cls : "", 
22960                     cn: [
22961                         inputblock
22962                     ]
22963                 }
22964             ];
22965             
22966             if (boxLabelCfg) {
22967                 cfg.cn[1].cn.push(boxLabelCfg);
22968             }
22969             
22970             if(this.labelWidth > 12){
22971                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22972             }
22973             
22974             if(this.labelWidth < 13 && this.labelmd == 0){
22975                 this.labelmd = this.labelWidth;
22976             }
22977             
22978             if(this.labellg > 0){
22979                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22980                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22981             }
22982             
22983             if(this.labelmd > 0){
22984                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22985                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22986             }
22987             
22988             if(this.labelsm > 0){
22989                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22990                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22991             }
22992             
22993             if(this.labelxs > 0){
22994                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22995                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22996             }
22997             
22998         } else if ( this.fieldLabel.length) {
22999 //                Roo.log(" label");
23000                 cfg.cn = [
23001                    
23002                     {
23003                         tag: this.boxLabel ? 'span' : 'label',
23004                         'for': id,
23005                         cls: 'control-label box-input-label',
23006                         //cls : 'input-group-addon',
23007                         html : this.fieldLabel
23008                     },
23009                     
23010                     inputblock
23011                     
23012                 ];
23013                 if (boxLabelCfg) {
23014                     cfg.cn.push(boxLabelCfg);
23015                 }
23016
23017         } else {
23018             
23019 //                Roo.log(" no label && no align");
23020                 cfg.cn = [  inputblock ] ;
23021                 if (boxLabelCfg) {
23022                     cfg.cn.push(boxLabelCfg);
23023                 }
23024
23025                 
23026         }
23027         
23028        
23029         
23030         if(this.inputType != 'radio'){
23031             cfg.cn.push(hidden);
23032         }
23033         
23034         return cfg;
23035         
23036     },
23037     
23038     /**
23039      * return the real input element.
23040      */
23041     inputEl: function ()
23042     {
23043         return this.el.select('input.roo-' + this.inputType,true).first();
23044     },
23045     hiddenEl: function ()
23046     {
23047         return this.el.select('input.roo-hidden-value',true).first();
23048     },
23049     
23050     labelEl: function()
23051     {
23052         return this.el.select('label.control-label',true).first();
23053     },
23054     /* depricated... */
23055     
23056     label: function()
23057     {
23058         return this.labelEl();
23059     },
23060     
23061     boxLabelEl: function()
23062     {
23063         return this.el.select('label.box-label',true).first();
23064     },
23065     
23066     initEvents : function()
23067     {
23068 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23069         
23070         this.inputEl().on('click', this.onClick,  this);
23071         
23072         if (this.boxLabel) { 
23073             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23074         }
23075         
23076         this.startValue = this.getValue();
23077         
23078         if(this.groupId){
23079             Roo.bootstrap.CheckBox.register(this);
23080         }
23081     },
23082     
23083     onClick : function(e)
23084     {   
23085         if(this.fireEvent('click', this, e) !== false){
23086             this.setChecked(!this.checked);
23087         }
23088         
23089     },
23090     
23091     setChecked : function(state,suppressEvent)
23092     {
23093         this.startValue = this.getValue();
23094
23095         if(this.inputType == 'radio'){
23096             
23097             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23098                 e.dom.checked = false;
23099             });
23100             
23101             this.inputEl().dom.checked = true;
23102             
23103             this.inputEl().dom.value = this.inputValue;
23104             
23105             if(suppressEvent !== true){
23106                 this.fireEvent('check', this, true);
23107             }
23108             
23109             this.validate();
23110             
23111             return;
23112         }
23113         
23114         this.checked = state;
23115         
23116         this.inputEl().dom.checked = state;
23117         
23118         
23119         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23120         
23121         if(suppressEvent !== true){
23122             this.fireEvent('check', this, state);
23123         }
23124         
23125         this.validate();
23126     },
23127     
23128     getValue : function()
23129     {
23130         if(this.inputType == 'radio'){
23131             return this.getGroupValue();
23132         }
23133         
23134         return this.hiddenEl().dom.value;
23135         
23136     },
23137     
23138     getGroupValue : function()
23139     {
23140         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23141             return '';
23142         }
23143         
23144         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23145     },
23146     
23147     setValue : function(v,suppressEvent)
23148     {
23149         if(this.inputType == 'radio'){
23150             this.setGroupValue(v, suppressEvent);
23151             return;
23152         }
23153         
23154         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23155         
23156         this.validate();
23157     },
23158     
23159     setGroupValue : function(v, suppressEvent)
23160     {
23161         this.startValue = this.getValue();
23162         
23163         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23164             e.dom.checked = false;
23165             
23166             if(e.dom.value == v){
23167                 e.dom.checked = true;
23168             }
23169         });
23170         
23171         if(suppressEvent !== true){
23172             this.fireEvent('check', this, true);
23173         }
23174
23175         this.validate();
23176         
23177         return;
23178     },
23179     
23180     validate : function()
23181     {
23182         if(this.getVisibilityEl().hasClass('hidden')){
23183             return true;
23184         }
23185         
23186         if(
23187                 this.disabled || 
23188                 (this.inputType == 'radio' && this.validateRadio()) ||
23189                 (this.inputType == 'checkbox' && this.validateCheckbox())
23190         ){
23191             this.markValid();
23192             return true;
23193         }
23194         
23195         this.markInvalid();
23196         return false;
23197     },
23198     
23199     validateRadio : function()
23200     {
23201         if(this.getVisibilityEl().hasClass('hidden')){
23202             return true;
23203         }
23204         
23205         if(this.allowBlank){
23206             return true;
23207         }
23208         
23209         var valid = false;
23210         
23211         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23212             if(!e.dom.checked){
23213                 return;
23214             }
23215             
23216             valid = true;
23217             
23218             return false;
23219         });
23220         
23221         return valid;
23222     },
23223     
23224     validateCheckbox : function()
23225     {
23226         if(!this.groupId){
23227             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23228             //return (this.getValue() == this.inputValue) ? true : false;
23229         }
23230         
23231         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23232         
23233         if(!group){
23234             return false;
23235         }
23236         
23237         var r = false;
23238         
23239         for(var i in group){
23240             if(group[i].el.isVisible(true)){
23241                 r = false;
23242                 break;
23243             }
23244             
23245             r = true;
23246         }
23247         
23248         for(var i in group){
23249             if(r){
23250                 break;
23251             }
23252             
23253             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23254         }
23255         
23256         return r;
23257     },
23258     
23259     /**
23260      * Mark this field as valid
23261      */
23262     markValid : function()
23263     {
23264         var _this = this;
23265         
23266         this.fireEvent('valid', this);
23267         
23268         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23269         
23270         if(this.groupId){
23271             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23272         }
23273         
23274         if(label){
23275             label.markValid();
23276         }
23277
23278         if(this.inputType == 'radio'){
23279             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23280                 var fg = e.findParent('.form-group', false, true);
23281                 if (Roo.bootstrap.version == 3) {
23282                     fg.removeClass([_this.invalidClass, _this.validClass]);
23283                     fg.addClass(_this.validClass);
23284                 } else {
23285                     fg.removeClass(['is-valid', 'is-invalid']);
23286                     fg.addClass('is-valid');
23287                 }
23288             });
23289             
23290             return;
23291         }
23292
23293         if(!this.groupId){
23294             var fg = this.el.findParent('.form-group', false, true);
23295             if (Roo.bootstrap.version == 3) {
23296                 fg.removeClass([this.invalidClass, this.validClass]);
23297                 fg.addClass(this.validClass);
23298             } else {
23299                 fg.removeClass(['is-valid', 'is-invalid']);
23300                 fg.addClass('is-valid');
23301             }
23302             return;
23303         }
23304         
23305         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23306         
23307         if(!group){
23308             return;
23309         }
23310         
23311         for(var i in group){
23312             var fg = group[i].el.findParent('.form-group', false, true);
23313             if (Roo.bootstrap.version == 3) {
23314                 fg.removeClass([this.invalidClass, this.validClass]);
23315                 fg.addClass(this.validClass);
23316             } else {
23317                 fg.removeClass(['is-valid', 'is-invalid']);
23318                 fg.addClass('is-valid');
23319             }
23320         }
23321     },
23322     
23323      /**
23324      * Mark this field as invalid
23325      * @param {String} msg The validation message
23326      */
23327     markInvalid : function(msg)
23328     {
23329         if(this.allowBlank){
23330             return;
23331         }
23332         
23333         var _this = this;
23334         
23335         this.fireEvent('invalid', this, msg);
23336         
23337         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23338         
23339         if(this.groupId){
23340             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23341         }
23342         
23343         if(label){
23344             label.markInvalid();
23345         }
23346             
23347         if(this.inputType == 'radio'){
23348             
23349             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23350                 var fg = e.findParent('.form-group', false, true);
23351                 if (Roo.bootstrap.version == 3) {
23352                     fg.removeClass([_this.invalidClass, _this.validClass]);
23353                     fg.addClass(_this.invalidClass);
23354                 } else {
23355                     fg.removeClass(['is-invalid', 'is-valid']);
23356                     fg.addClass('is-invalid');
23357                 }
23358             });
23359             
23360             return;
23361         }
23362         
23363         if(!this.groupId){
23364             var fg = this.el.findParent('.form-group', false, true);
23365             if (Roo.bootstrap.version == 3) {
23366                 fg.removeClass([_this.invalidClass, _this.validClass]);
23367                 fg.addClass(_this.invalidClass);
23368             } else {
23369                 fg.removeClass(['is-invalid', 'is-valid']);
23370                 fg.addClass('is-invalid');
23371             }
23372             return;
23373         }
23374         
23375         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23376         
23377         if(!group){
23378             return;
23379         }
23380         
23381         for(var i in group){
23382             var fg = group[i].el.findParent('.form-group', false, true);
23383             if (Roo.bootstrap.version == 3) {
23384                 fg.removeClass([_this.invalidClass, _this.validClass]);
23385                 fg.addClass(_this.invalidClass);
23386             } else {
23387                 fg.removeClass(['is-invalid', 'is-valid']);
23388                 fg.addClass('is-invalid');
23389             }
23390         }
23391         
23392     },
23393     
23394     clearInvalid : function()
23395     {
23396         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23397         
23398         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23399         
23400         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23401         
23402         if (label && label.iconEl) {
23403             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23404             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23405         }
23406     },
23407     
23408     disable : function()
23409     {
23410         if(this.inputType != 'radio'){
23411             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23412             return;
23413         }
23414         
23415         var _this = this;
23416         
23417         if(this.rendered){
23418             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23419                 _this.getActionEl().addClass(this.disabledClass);
23420                 e.dom.disabled = true;
23421             });
23422         }
23423         
23424         this.disabled = true;
23425         this.fireEvent("disable", this);
23426         return this;
23427     },
23428
23429     enable : function()
23430     {
23431         if(this.inputType != 'radio'){
23432             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23433             return;
23434         }
23435         
23436         var _this = this;
23437         
23438         if(this.rendered){
23439             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23440                 _this.getActionEl().removeClass(this.disabledClass);
23441                 e.dom.disabled = false;
23442             });
23443         }
23444         
23445         this.disabled = false;
23446         this.fireEvent("enable", this);
23447         return this;
23448     },
23449     
23450     setBoxLabel : function(v)
23451     {
23452         this.boxLabel = v;
23453         
23454         if(this.rendered){
23455             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23456         }
23457     }
23458
23459 });
23460
23461 Roo.apply(Roo.bootstrap.CheckBox, {
23462     
23463     groups: {},
23464     
23465      /**
23466     * register a CheckBox Group
23467     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23468     */
23469     register : function(checkbox)
23470     {
23471         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23472             this.groups[checkbox.groupId] = {};
23473         }
23474         
23475         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23476             return;
23477         }
23478         
23479         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23480         
23481     },
23482     /**
23483     * fetch a CheckBox Group based on the group ID
23484     * @param {string} the group ID
23485     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23486     */
23487     get: function(groupId) {
23488         if (typeof(this.groups[groupId]) == 'undefined') {
23489             return false;
23490         }
23491         
23492         return this.groups[groupId] ;
23493     }
23494     
23495     
23496 });
23497 /*
23498  * - LGPL
23499  *
23500  * RadioItem
23501  * 
23502  */
23503
23504 /**
23505  * @class Roo.bootstrap.Radio
23506  * @extends Roo.bootstrap.Component
23507  * Bootstrap Radio class
23508  * @cfg {String} boxLabel - the label associated
23509  * @cfg {String} value - the value of radio
23510  * 
23511  * @constructor
23512  * Create a new Radio
23513  * @param {Object} config The config object
23514  */
23515 Roo.bootstrap.Radio = function(config){
23516     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23517     
23518 };
23519
23520 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23521     
23522     boxLabel : '',
23523     
23524     value : '',
23525     
23526     getAutoCreate : function()
23527     {
23528         var cfg = {
23529             tag : 'div',
23530             cls : 'form-group radio',
23531             cn : [
23532                 {
23533                     tag : 'label',
23534                     cls : 'box-label',
23535                     html : this.boxLabel
23536                 }
23537             ]
23538         };
23539         
23540         return cfg;
23541     },
23542     
23543     initEvents : function() 
23544     {
23545         this.parent().register(this);
23546         
23547         this.el.on('click', this.onClick, this);
23548         
23549     },
23550     
23551     onClick : function(e)
23552     {
23553         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23554             this.setChecked(true);
23555         }
23556     },
23557     
23558     setChecked : function(state, suppressEvent)
23559     {
23560         this.parent().setValue(this.value, suppressEvent);
23561         
23562     },
23563     
23564     setBoxLabel : function(v)
23565     {
23566         this.boxLabel = v;
23567         
23568         if(this.rendered){
23569             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23570         }
23571     }
23572     
23573 });
23574  
23575
23576  /*
23577  * - LGPL
23578  *
23579  * Input
23580  * 
23581  */
23582
23583 /**
23584  * @class Roo.bootstrap.SecurePass
23585  * @extends Roo.bootstrap.Input
23586  * Bootstrap SecurePass class
23587  *
23588  * 
23589  * @constructor
23590  * Create a new SecurePass
23591  * @param {Object} config The config object
23592  */
23593  
23594 Roo.bootstrap.SecurePass = function (config) {
23595     // these go here, so the translation tool can replace them..
23596     this.errors = {
23597         PwdEmpty: "Please type a password, and then retype it to confirm.",
23598         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23599         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23600         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23601         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23602         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23603         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23604         TooWeak: "Your password is Too Weak."
23605     },
23606     this.meterLabel = "Password strength:";
23607     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23608     this.meterClass = [
23609         "roo-password-meter-tooweak", 
23610         "roo-password-meter-weak", 
23611         "roo-password-meter-medium", 
23612         "roo-password-meter-strong", 
23613         "roo-password-meter-grey"
23614     ];
23615     
23616     this.errors = {};
23617     
23618     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23619 }
23620
23621 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23622     /**
23623      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23624      * {
23625      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23626      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23627      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23628      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23629      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23630      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23631      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23632      * })
23633      */
23634     // private
23635     
23636     meterWidth: 300,
23637     errorMsg :'',    
23638     errors: false,
23639     imageRoot: '/',
23640     /**
23641      * @cfg {String/Object} Label for the strength meter (defaults to
23642      * 'Password strength:')
23643      */
23644     // private
23645     meterLabel: '',
23646     /**
23647      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23648      * ['Weak', 'Medium', 'Strong'])
23649      */
23650     // private    
23651     pwdStrengths: false,    
23652     // private
23653     strength: 0,
23654     // private
23655     _lastPwd: null,
23656     // private
23657     kCapitalLetter: 0,
23658     kSmallLetter: 1,
23659     kDigit: 2,
23660     kPunctuation: 3,
23661     
23662     insecure: false,
23663     // private
23664     initEvents: function ()
23665     {
23666         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23667
23668         if (this.el.is('input[type=password]') && Roo.isSafari) {
23669             this.el.on('keydown', this.SafariOnKeyDown, this);
23670         }
23671
23672         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23673     },
23674     // private
23675     onRender: function (ct, position)
23676     {
23677         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23678         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23679         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23680
23681         this.trigger.createChild({
23682                    cn: [
23683                     {
23684                     //id: 'PwdMeter',
23685                     tag: 'div',
23686                     cls: 'roo-password-meter-grey col-xs-12',
23687                     style: {
23688                         //width: 0,
23689                         //width: this.meterWidth + 'px'                                                
23690                         }
23691                     },
23692                     {                            
23693                          cls: 'roo-password-meter-text'                          
23694                     }
23695                 ]            
23696         });
23697
23698          
23699         if (this.hideTrigger) {
23700             this.trigger.setDisplayed(false);
23701         }
23702         this.setSize(this.width || '', this.height || '');
23703     },
23704     // private
23705     onDestroy: function ()
23706     {
23707         if (this.trigger) {
23708             this.trigger.removeAllListeners();
23709             this.trigger.remove();
23710         }
23711         if (this.wrap) {
23712             this.wrap.remove();
23713         }
23714         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23715     },
23716     // private
23717     checkStrength: function ()
23718     {
23719         var pwd = this.inputEl().getValue();
23720         if (pwd == this._lastPwd) {
23721             return;
23722         }
23723
23724         var strength;
23725         if (this.ClientSideStrongPassword(pwd)) {
23726             strength = 3;
23727         } else if (this.ClientSideMediumPassword(pwd)) {
23728             strength = 2;
23729         } else if (this.ClientSideWeakPassword(pwd)) {
23730             strength = 1;
23731         } else {
23732             strength = 0;
23733         }
23734         
23735         Roo.log('strength1: ' + strength);
23736         
23737         //var pm = this.trigger.child('div/div/div').dom;
23738         var pm = this.trigger.child('div/div');
23739         pm.removeClass(this.meterClass);
23740         pm.addClass(this.meterClass[strength]);
23741                 
23742         
23743         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23744                 
23745         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23746         
23747         this._lastPwd = pwd;
23748     },
23749     reset: function ()
23750     {
23751         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23752         
23753         this._lastPwd = '';
23754         
23755         var pm = this.trigger.child('div/div');
23756         pm.removeClass(this.meterClass);
23757         pm.addClass('roo-password-meter-grey');        
23758         
23759         
23760         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23761         
23762         pt.innerHTML = '';
23763         this.inputEl().dom.type='password';
23764     },
23765     // private
23766     validateValue: function (value)
23767     {
23768         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23769             return false;
23770         }
23771         if (value.length == 0) {
23772             if (this.allowBlank) {
23773                 this.clearInvalid();
23774                 return true;
23775             }
23776
23777             this.markInvalid(this.errors.PwdEmpty);
23778             this.errorMsg = this.errors.PwdEmpty;
23779             return false;
23780         }
23781         
23782         if(this.insecure){
23783             return true;
23784         }
23785         
23786         if (!value.match(/[\x21-\x7e]+/)) {
23787             this.markInvalid(this.errors.PwdBadChar);
23788             this.errorMsg = this.errors.PwdBadChar;
23789             return false;
23790         }
23791         if (value.length < 6) {
23792             this.markInvalid(this.errors.PwdShort);
23793             this.errorMsg = this.errors.PwdShort;
23794             return false;
23795         }
23796         if (value.length > 16) {
23797             this.markInvalid(this.errors.PwdLong);
23798             this.errorMsg = this.errors.PwdLong;
23799             return false;
23800         }
23801         var strength;
23802         if (this.ClientSideStrongPassword(value)) {
23803             strength = 3;
23804         } else if (this.ClientSideMediumPassword(value)) {
23805             strength = 2;
23806         } else if (this.ClientSideWeakPassword(value)) {
23807             strength = 1;
23808         } else {
23809             strength = 0;
23810         }
23811
23812         
23813         if (strength < 2) {
23814             //this.markInvalid(this.errors.TooWeak);
23815             this.errorMsg = this.errors.TooWeak;
23816             //return false;
23817         }
23818         
23819         
23820         console.log('strength2: ' + strength);
23821         
23822         //var pm = this.trigger.child('div/div/div').dom;
23823         
23824         var pm = this.trigger.child('div/div');
23825         pm.removeClass(this.meterClass);
23826         pm.addClass(this.meterClass[strength]);
23827                 
23828         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23829                 
23830         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23831         
23832         this.errorMsg = ''; 
23833         return true;
23834     },
23835     // private
23836     CharacterSetChecks: function (type)
23837     {
23838         this.type = type;
23839         this.fResult = false;
23840     },
23841     // private
23842     isctype: function (character, type)
23843     {
23844         switch (type) {  
23845             case this.kCapitalLetter:
23846                 if (character >= 'A' && character <= 'Z') {
23847                     return true;
23848                 }
23849                 break;
23850             
23851             case this.kSmallLetter:
23852                 if (character >= 'a' && character <= 'z') {
23853                     return true;
23854                 }
23855                 break;
23856             
23857             case this.kDigit:
23858                 if (character >= '0' && character <= '9') {
23859                     return true;
23860                 }
23861                 break;
23862             
23863             case this.kPunctuation:
23864                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23865                     return true;
23866                 }
23867                 break;
23868             
23869             default:
23870                 return false;
23871         }
23872
23873     },
23874     // private
23875     IsLongEnough: function (pwd, size)
23876     {
23877         return !(pwd == null || isNaN(size) || pwd.length < size);
23878     },
23879     // private
23880     SpansEnoughCharacterSets: function (word, nb)
23881     {
23882         if (!this.IsLongEnough(word, nb))
23883         {
23884             return false;
23885         }
23886
23887         var characterSetChecks = new Array(
23888             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23889             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23890         );
23891         
23892         for (var index = 0; index < word.length; ++index) {
23893             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23894                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23895                     characterSetChecks[nCharSet].fResult = true;
23896                     break;
23897                 }
23898             }
23899         }
23900
23901         var nCharSets = 0;
23902         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23903             if (characterSetChecks[nCharSet].fResult) {
23904                 ++nCharSets;
23905             }
23906         }
23907
23908         if (nCharSets < nb) {
23909             return false;
23910         }
23911         return true;
23912     },
23913     // private
23914     ClientSideStrongPassword: function (pwd)
23915     {
23916         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23917     },
23918     // private
23919     ClientSideMediumPassword: function (pwd)
23920     {
23921         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23922     },
23923     // private
23924     ClientSideWeakPassword: function (pwd)
23925     {
23926         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23927     }
23928           
23929 })//<script type="text/javascript">
23930
23931 /*
23932  * Based  Ext JS Library 1.1.1
23933  * Copyright(c) 2006-2007, Ext JS, LLC.
23934  * LGPL
23935  *
23936  */
23937  
23938 /**
23939  * @class Roo.HtmlEditorCore
23940  * @extends Roo.Component
23941  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23942  *
23943  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23944  */
23945
23946 Roo.HtmlEditorCore = function(config){
23947     
23948     
23949     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23950     
23951     
23952     this.addEvents({
23953         /**
23954          * @event initialize
23955          * Fires when the editor is fully initialized (including the iframe)
23956          * @param {Roo.HtmlEditorCore} this
23957          */
23958         initialize: true,
23959         /**
23960          * @event activate
23961          * Fires when the editor is first receives the focus. Any insertion must wait
23962          * until after this event.
23963          * @param {Roo.HtmlEditorCore} this
23964          */
23965         activate: true,
23966          /**
23967          * @event beforesync
23968          * Fires before the textarea is updated with content from the editor iframe. Return false
23969          * to cancel the sync.
23970          * @param {Roo.HtmlEditorCore} this
23971          * @param {String} html
23972          */
23973         beforesync: true,
23974          /**
23975          * @event beforepush
23976          * Fires before the iframe editor is updated with content from the textarea. Return false
23977          * to cancel the push.
23978          * @param {Roo.HtmlEditorCore} this
23979          * @param {String} html
23980          */
23981         beforepush: true,
23982          /**
23983          * @event sync
23984          * Fires when the textarea is updated with content from the editor iframe.
23985          * @param {Roo.HtmlEditorCore} this
23986          * @param {String} html
23987          */
23988         sync: true,
23989          /**
23990          * @event push
23991          * Fires when the iframe editor is updated with content from the textarea.
23992          * @param {Roo.HtmlEditorCore} this
23993          * @param {String} html
23994          */
23995         push: true,
23996         
23997         /**
23998          * @event editorevent
23999          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
24000          * @param {Roo.HtmlEditorCore} this
24001          */
24002         editorevent: true
24003         
24004     });
24005     
24006     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24007     
24008     // defaults : white / black...
24009     this.applyBlacklists();
24010     
24011     
24012     
24013 };
24014
24015
24016 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24017
24018
24019      /**
24020      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24021      */
24022     
24023     owner : false,
24024     
24025      /**
24026      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24027      *                        Roo.resizable.
24028      */
24029     resizable : false,
24030      /**
24031      * @cfg {Number} height (in pixels)
24032      */   
24033     height: 300,
24034    /**
24035      * @cfg {Number} width (in pixels)
24036      */   
24037     width: 500,
24038     
24039     /**
24040      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24041      * 
24042      */
24043     stylesheets: false,
24044     
24045     // id of frame..
24046     frameId: false,
24047     
24048     // private properties
24049     validationEvent : false,
24050     deferHeight: true,
24051     initialized : false,
24052     activated : false,
24053     sourceEditMode : false,
24054     onFocus : Roo.emptyFn,
24055     iframePad:3,
24056     hideMode:'offsets',
24057     
24058     clearUp: true,
24059     
24060     // blacklist + whitelisted elements..
24061     black: false,
24062     white: false,
24063      
24064     bodyCls : '',
24065
24066     /**
24067      * Protected method that will not generally be called directly. It
24068      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24069      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24070      */
24071     getDocMarkup : function(){
24072         // body styles..
24073         var st = '';
24074         
24075         // inherit styels from page...?? 
24076         if (this.stylesheets === false) {
24077             
24078             Roo.get(document.head).select('style').each(function(node) {
24079                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24080             });
24081             
24082             Roo.get(document.head).select('link').each(function(node) { 
24083                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24084             });
24085             
24086         } else if (!this.stylesheets.length) {
24087                 // simple..
24088                 st = '<style type="text/css">' +
24089                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24090                    '</style>';
24091         } else {
24092             for (var i in this.stylesheets) { 
24093                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24094             }
24095             
24096         }
24097         
24098         st +=  '<style type="text/css">' +
24099             'IMG { cursor: pointer } ' +
24100         '</style>';
24101
24102         var cls = 'roo-htmleditor-body';
24103         
24104         if(this.bodyCls.length){
24105             cls += ' ' + this.bodyCls;
24106         }
24107         
24108         return '<html><head>' + st  +
24109             //<style type="text/css">' +
24110             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24111             //'</style>' +
24112             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24113     },
24114
24115     // private
24116     onRender : function(ct, position)
24117     {
24118         var _t = this;
24119         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24120         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24121         
24122         
24123         this.el.dom.style.border = '0 none';
24124         this.el.dom.setAttribute('tabIndex', -1);
24125         this.el.addClass('x-hidden hide');
24126         
24127         
24128         
24129         if(Roo.isIE){ // fix IE 1px bogus margin
24130             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24131         }
24132        
24133         
24134         this.frameId = Roo.id();
24135         
24136          
24137         
24138         var iframe = this.owner.wrap.createChild({
24139             tag: 'iframe',
24140             cls: 'form-control', // bootstrap..
24141             id: this.frameId,
24142             name: this.frameId,
24143             frameBorder : 'no',
24144             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24145         }, this.el
24146         );
24147         
24148         
24149         this.iframe = iframe.dom;
24150
24151          this.assignDocWin();
24152         
24153         this.doc.designMode = 'on';
24154        
24155         this.doc.open();
24156         this.doc.write(this.getDocMarkup());
24157         this.doc.close();
24158
24159         
24160         var task = { // must defer to wait for browser to be ready
24161             run : function(){
24162                 //console.log("run task?" + this.doc.readyState);
24163                 this.assignDocWin();
24164                 if(this.doc.body || this.doc.readyState == 'complete'){
24165                     try {
24166                         this.doc.designMode="on";
24167                     } catch (e) {
24168                         return;
24169                     }
24170                     Roo.TaskMgr.stop(task);
24171                     this.initEditor.defer(10, this);
24172                 }
24173             },
24174             interval : 10,
24175             duration: 10000,
24176             scope: this
24177         };
24178         Roo.TaskMgr.start(task);
24179
24180     },
24181
24182     // private
24183     onResize : function(w, h)
24184     {
24185          Roo.log('resize: ' +w + ',' + h );
24186         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24187         if(!this.iframe){
24188             return;
24189         }
24190         if(typeof w == 'number'){
24191             
24192             this.iframe.style.width = w + 'px';
24193         }
24194         if(typeof h == 'number'){
24195             
24196             this.iframe.style.height = h + 'px';
24197             if(this.doc){
24198                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24199             }
24200         }
24201         
24202     },
24203
24204     /**
24205      * Toggles the editor between standard and source edit mode.
24206      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24207      */
24208     toggleSourceEdit : function(sourceEditMode){
24209         
24210         this.sourceEditMode = sourceEditMode === true;
24211         
24212         if(this.sourceEditMode){
24213  
24214             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24215             
24216         }else{
24217             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24218             //this.iframe.className = '';
24219             this.deferFocus();
24220         }
24221         //this.setSize(this.owner.wrap.getSize());
24222         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24223     },
24224
24225     
24226   
24227
24228     /**
24229      * Protected method that will not generally be called directly. If you need/want
24230      * custom HTML cleanup, this is the method you should override.
24231      * @param {String} html The HTML to be cleaned
24232      * return {String} The cleaned HTML
24233      */
24234     cleanHtml : function(html){
24235         html = String(html);
24236         if(html.length > 5){
24237             if(Roo.isSafari){ // strip safari nonsense
24238                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24239             }
24240         }
24241         if(html == '&nbsp;'){
24242             html = '';
24243         }
24244         return html;
24245     },
24246
24247     /**
24248      * HTML Editor -> Textarea
24249      * Protected method that will not generally be called directly. Syncs the contents
24250      * of the editor iframe with the textarea.
24251      */
24252     syncValue : function(){
24253         if(this.initialized){
24254             var bd = (this.doc.body || this.doc.documentElement);
24255             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24256             var html = bd.innerHTML;
24257             if(Roo.isSafari){
24258                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24259                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24260                 if(m && m[1]){
24261                     html = '<div style="'+m[0]+'">' + html + '</div>';
24262                 }
24263             }
24264             html = this.cleanHtml(html);
24265             // fix up the special chars.. normaly like back quotes in word...
24266             // however we do not want to do this with chinese..
24267             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24268                 
24269                 var cc = match.charCodeAt();
24270
24271                 // Get the character value, handling surrogate pairs
24272                 if (match.length == 2) {
24273                     // It's a surrogate pair, calculate the Unicode code point
24274                     var high = match.charCodeAt(0) - 0xD800;
24275                     var low  = match.charCodeAt(1) - 0xDC00;
24276                     cc = (high * 0x400) + low + 0x10000;
24277                 }  else if (
24278                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24279                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24280                     (cc >= 0xf900 && cc < 0xfb00 )
24281                 ) {
24282                         return match;
24283                 }  
24284          
24285                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24286                 return "&#" + cc + ";";
24287                 
24288                 
24289             });
24290             
24291             
24292              
24293             if(this.owner.fireEvent('beforesync', this, html) !== false){
24294                 this.el.dom.value = html;
24295                 this.owner.fireEvent('sync', this, html);
24296             }
24297         }
24298     },
24299
24300     /**
24301      * Protected method that will not generally be called directly. Pushes the value of the textarea
24302      * into the iframe editor.
24303      */
24304     pushValue : function(){
24305         if(this.initialized){
24306             var v = this.el.dom.value.trim();
24307             
24308 //            if(v.length < 1){
24309 //                v = '&#160;';
24310 //            }
24311             
24312             if(this.owner.fireEvent('beforepush', this, v) !== false){
24313                 var d = (this.doc.body || this.doc.documentElement);
24314                 d.innerHTML = v;
24315                 this.cleanUpPaste();
24316                 this.el.dom.value = d.innerHTML;
24317                 this.owner.fireEvent('push', this, v);
24318             }
24319         }
24320     },
24321
24322     // private
24323     deferFocus : function(){
24324         this.focus.defer(10, this);
24325     },
24326
24327     // doc'ed in Field
24328     focus : function(){
24329         if(this.win && !this.sourceEditMode){
24330             this.win.focus();
24331         }else{
24332             this.el.focus();
24333         }
24334     },
24335     
24336     assignDocWin: function()
24337     {
24338         var iframe = this.iframe;
24339         
24340          if(Roo.isIE){
24341             this.doc = iframe.contentWindow.document;
24342             this.win = iframe.contentWindow;
24343         } else {
24344 //            if (!Roo.get(this.frameId)) {
24345 //                return;
24346 //            }
24347 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24348 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24349             
24350             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24351                 return;
24352             }
24353             
24354             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24355             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24356         }
24357     },
24358     
24359     // private
24360     initEditor : function(){
24361         //console.log("INIT EDITOR");
24362         this.assignDocWin();
24363         
24364         
24365         
24366         this.doc.designMode="on";
24367         this.doc.open();
24368         this.doc.write(this.getDocMarkup());
24369         this.doc.close();
24370         
24371         var dbody = (this.doc.body || this.doc.documentElement);
24372         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24373         // this copies styles from the containing element into thsi one..
24374         // not sure why we need all of this..
24375         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24376         
24377         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24378         //ss['background-attachment'] = 'fixed'; // w3c
24379         dbody.bgProperties = 'fixed'; // ie
24380         //Roo.DomHelper.applyStyles(dbody, ss);
24381         Roo.EventManager.on(this.doc, {
24382             //'mousedown': this.onEditorEvent,
24383             'mouseup': this.onEditorEvent,
24384             'dblclick': this.onEditorEvent,
24385             'click': this.onEditorEvent,
24386             'keyup': this.onEditorEvent,
24387             buffer:100,
24388             scope: this
24389         });
24390         if(Roo.isGecko){
24391             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24392         }
24393         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24394             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24395         }
24396         this.initialized = true;
24397
24398         this.owner.fireEvent('initialize', this);
24399         this.pushValue();
24400     },
24401
24402     // private
24403     onDestroy : function(){
24404         
24405         
24406         
24407         if(this.rendered){
24408             
24409             //for (var i =0; i < this.toolbars.length;i++) {
24410             //    // fixme - ask toolbars for heights?
24411             //    this.toolbars[i].onDestroy();
24412            // }
24413             
24414             //this.wrap.dom.innerHTML = '';
24415             //this.wrap.remove();
24416         }
24417     },
24418
24419     // private
24420     onFirstFocus : function(){
24421         
24422         this.assignDocWin();
24423         
24424         
24425         this.activated = true;
24426          
24427     
24428         if(Roo.isGecko){ // prevent silly gecko errors
24429             this.win.focus();
24430             var s = this.win.getSelection();
24431             if(!s.focusNode || s.focusNode.nodeType != 3){
24432                 var r = s.getRangeAt(0);
24433                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24434                 r.collapse(true);
24435                 this.deferFocus();
24436             }
24437             try{
24438                 this.execCmd('useCSS', true);
24439                 this.execCmd('styleWithCSS', false);
24440             }catch(e){}
24441         }
24442         this.owner.fireEvent('activate', this);
24443     },
24444
24445     // private
24446     adjustFont: function(btn){
24447         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24448         //if(Roo.isSafari){ // safari
24449         //    adjust *= 2;
24450        // }
24451         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24452         if(Roo.isSafari){ // safari
24453             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24454             v =  (v < 10) ? 10 : v;
24455             v =  (v > 48) ? 48 : v;
24456             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24457             
24458         }
24459         
24460         
24461         v = Math.max(1, v+adjust);
24462         
24463         this.execCmd('FontSize', v  );
24464     },
24465
24466     onEditorEvent : function(e)
24467     {
24468         this.owner.fireEvent('editorevent', this, e);
24469       //  this.updateToolbar();
24470         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24471     },
24472
24473     insertTag : function(tg)
24474     {
24475         // could be a bit smarter... -> wrap the current selected tRoo..
24476         if (tg.toLowerCase() == 'span' ||
24477             tg.toLowerCase() == 'code' ||
24478             tg.toLowerCase() == 'sup' ||
24479             tg.toLowerCase() == 'sub' 
24480             ) {
24481             
24482             range = this.createRange(this.getSelection());
24483             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24484             wrappingNode.appendChild(range.extractContents());
24485             range.insertNode(wrappingNode);
24486
24487             return;
24488             
24489             
24490             
24491         }
24492         this.execCmd("formatblock",   tg);
24493         
24494     },
24495     
24496     insertText : function(txt)
24497     {
24498         
24499         
24500         var range = this.createRange();
24501         range.deleteContents();
24502                //alert(Sender.getAttribute('label'));
24503                
24504         range.insertNode(this.doc.createTextNode(txt));
24505     } ,
24506     
24507      
24508
24509     /**
24510      * Executes a Midas editor command on the editor document and performs necessary focus and
24511      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24512      * @param {String} cmd The Midas command
24513      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24514      */
24515     relayCmd : function(cmd, value){
24516         this.win.focus();
24517         this.execCmd(cmd, value);
24518         this.owner.fireEvent('editorevent', this);
24519         //this.updateToolbar();
24520         this.owner.deferFocus();
24521     },
24522
24523     /**
24524      * Executes a Midas editor command directly on the editor document.
24525      * For visual commands, you should use {@link #relayCmd} instead.
24526      * <b>This should only be called after the editor is initialized.</b>
24527      * @param {String} cmd The Midas command
24528      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24529      */
24530     execCmd : function(cmd, value){
24531         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24532         this.syncValue();
24533     },
24534  
24535  
24536    
24537     /**
24538      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24539      * to insert tRoo.
24540      * @param {String} text | dom node.. 
24541      */
24542     insertAtCursor : function(text)
24543     {
24544         
24545         if(!this.activated){
24546             return;
24547         }
24548         /*
24549         if(Roo.isIE){
24550             this.win.focus();
24551             var r = this.doc.selection.createRange();
24552             if(r){
24553                 r.collapse(true);
24554                 r.pasteHTML(text);
24555                 this.syncValue();
24556                 this.deferFocus();
24557             
24558             }
24559             return;
24560         }
24561         */
24562         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24563             this.win.focus();
24564             
24565             
24566             // from jquery ui (MIT licenced)
24567             var range, node;
24568             var win = this.win;
24569             
24570             if (win.getSelection && win.getSelection().getRangeAt) {
24571                 range = win.getSelection().getRangeAt(0);
24572                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24573                 range.insertNode(node);
24574             } else if (win.document.selection && win.document.selection.createRange) {
24575                 // no firefox support
24576                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24577                 win.document.selection.createRange().pasteHTML(txt);
24578             } else {
24579                 // no firefox support
24580                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24581                 this.execCmd('InsertHTML', txt);
24582             } 
24583             
24584             this.syncValue();
24585             
24586             this.deferFocus();
24587         }
24588     },
24589  // private
24590     mozKeyPress : function(e){
24591         if(e.ctrlKey){
24592             var c = e.getCharCode(), cmd;
24593           
24594             if(c > 0){
24595                 c = String.fromCharCode(c).toLowerCase();
24596                 switch(c){
24597                     case 'b':
24598                         cmd = 'bold';
24599                         break;
24600                     case 'i':
24601                         cmd = 'italic';
24602                         break;
24603                     
24604                     case 'u':
24605                         cmd = 'underline';
24606                         break;
24607                     
24608                     case 'v':
24609                         this.cleanUpPaste.defer(100, this);
24610                         return;
24611                         
24612                 }
24613                 if(cmd){
24614                     this.win.focus();
24615                     this.execCmd(cmd);
24616                     this.deferFocus();
24617                     e.preventDefault();
24618                 }
24619                 
24620             }
24621         }
24622     },
24623
24624     // private
24625     fixKeys : function(){ // load time branching for fastest keydown performance
24626         if(Roo.isIE){
24627             return function(e){
24628                 var k = e.getKey(), r;
24629                 if(k == e.TAB){
24630                     e.stopEvent();
24631                     r = this.doc.selection.createRange();
24632                     if(r){
24633                         r.collapse(true);
24634                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24635                         this.deferFocus();
24636                     }
24637                     return;
24638                 }
24639                 
24640                 if(k == e.ENTER){
24641                     r = this.doc.selection.createRange();
24642                     if(r){
24643                         var target = r.parentElement();
24644                         if(!target || target.tagName.toLowerCase() != 'li'){
24645                             e.stopEvent();
24646                             r.pasteHTML('<br />');
24647                             r.collapse(false);
24648                             r.select();
24649                         }
24650                     }
24651                 }
24652                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24653                     this.cleanUpPaste.defer(100, this);
24654                     return;
24655                 }
24656                 
24657                 
24658             };
24659         }else if(Roo.isOpera){
24660             return function(e){
24661                 var k = e.getKey();
24662                 if(k == e.TAB){
24663                     e.stopEvent();
24664                     this.win.focus();
24665                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24666                     this.deferFocus();
24667                 }
24668                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24669                     this.cleanUpPaste.defer(100, this);
24670                     return;
24671                 }
24672                 
24673             };
24674         }else if(Roo.isSafari){
24675             return function(e){
24676                 var k = e.getKey();
24677                 
24678                 if(k == e.TAB){
24679                     e.stopEvent();
24680                     this.execCmd('InsertText','\t');
24681                     this.deferFocus();
24682                     return;
24683                 }
24684                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24685                     this.cleanUpPaste.defer(100, this);
24686                     return;
24687                 }
24688                 
24689              };
24690         }
24691     }(),
24692     
24693     getAllAncestors: function()
24694     {
24695         var p = this.getSelectedNode();
24696         var a = [];
24697         if (!p) {
24698             a.push(p); // push blank onto stack..
24699             p = this.getParentElement();
24700         }
24701         
24702         
24703         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24704             a.push(p);
24705             p = p.parentNode;
24706         }
24707         a.push(this.doc.body);
24708         return a;
24709     },
24710     lastSel : false,
24711     lastSelNode : false,
24712     
24713     
24714     getSelection : function() 
24715     {
24716         this.assignDocWin();
24717         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24718     },
24719     
24720     getSelectedNode: function() 
24721     {
24722         // this may only work on Gecko!!!
24723         
24724         // should we cache this!!!!
24725         
24726         
24727         
24728          
24729         var range = this.createRange(this.getSelection()).cloneRange();
24730         
24731         if (Roo.isIE) {
24732             var parent = range.parentElement();
24733             while (true) {
24734                 var testRange = range.duplicate();
24735                 testRange.moveToElementText(parent);
24736                 if (testRange.inRange(range)) {
24737                     break;
24738                 }
24739                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24740                     break;
24741                 }
24742                 parent = parent.parentElement;
24743             }
24744             return parent;
24745         }
24746         
24747         // is ancestor a text element.
24748         var ac =  range.commonAncestorContainer;
24749         if (ac.nodeType == 3) {
24750             ac = ac.parentNode;
24751         }
24752         
24753         var ar = ac.childNodes;
24754          
24755         var nodes = [];
24756         var other_nodes = [];
24757         var has_other_nodes = false;
24758         for (var i=0;i<ar.length;i++) {
24759             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24760                 continue;
24761             }
24762             // fullly contained node.
24763             
24764             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24765                 nodes.push(ar[i]);
24766                 continue;
24767             }
24768             
24769             // probably selected..
24770             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24771                 other_nodes.push(ar[i]);
24772                 continue;
24773             }
24774             // outer..
24775             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24776                 continue;
24777             }
24778             
24779             
24780             has_other_nodes = true;
24781         }
24782         if (!nodes.length && other_nodes.length) {
24783             nodes= other_nodes;
24784         }
24785         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24786             return false;
24787         }
24788         
24789         return nodes[0];
24790     },
24791     createRange: function(sel)
24792     {
24793         // this has strange effects when using with 
24794         // top toolbar - not sure if it's a great idea.
24795         //this.editor.contentWindow.focus();
24796         if (typeof sel != "undefined") {
24797             try {
24798                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24799             } catch(e) {
24800                 return this.doc.createRange();
24801             }
24802         } else {
24803             return this.doc.createRange();
24804         }
24805     },
24806     getParentElement: function()
24807     {
24808         
24809         this.assignDocWin();
24810         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24811         
24812         var range = this.createRange(sel);
24813          
24814         try {
24815             var p = range.commonAncestorContainer;
24816             while (p.nodeType == 3) { // text node
24817                 p = p.parentNode;
24818             }
24819             return p;
24820         } catch (e) {
24821             return null;
24822         }
24823     
24824     },
24825     /***
24826      *
24827      * Range intersection.. the hard stuff...
24828      *  '-1' = before
24829      *  '0' = hits..
24830      *  '1' = after.
24831      *         [ -- selected range --- ]
24832      *   [fail]                        [fail]
24833      *
24834      *    basically..
24835      *      if end is before start or  hits it. fail.
24836      *      if start is after end or hits it fail.
24837      *
24838      *   if either hits (but other is outside. - then it's not 
24839      *   
24840      *    
24841      **/
24842     
24843     
24844     // @see http://www.thismuchiknow.co.uk/?p=64.
24845     rangeIntersectsNode : function(range, node)
24846     {
24847         var nodeRange = node.ownerDocument.createRange();
24848         try {
24849             nodeRange.selectNode(node);
24850         } catch (e) {
24851             nodeRange.selectNodeContents(node);
24852         }
24853     
24854         var rangeStartRange = range.cloneRange();
24855         rangeStartRange.collapse(true);
24856     
24857         var rangeEndRange = range.cloneRange();
24858         rangeEndRange.collapse(false);
24859     
24860         var nodeStartRange = nodeRange.cloneRange();
24861         nodeStartRange.collapse(true);
24862     
24863         var nodeEndRange = nodeRange.cloneRange();
24864         nodeEndRange.collapse(false);
24865     
24866         return rangeStartRange.compareBoundaryPoints(
24867                  Range.START_TO_START, nodeEndRange) == -1 &&
24868                rangeEndRange.compareBoundaryPoints(
24869                  Range.START_TO_START, nodeStartRange) == 1;
24870         
24871          
24872     },
24873     rangeCompareNode : function(range, node)
24874     {
24875         var nodeRange = node.ownerDocument.createRange();
24876         try {
24877             nodeRange.selectNode(node);
24878         } catch (e) {
24879             nodeRange.selectNodeContents(node);
24880         }
24881         
24882         
24883         range.collapse(true);
24884     
24885         nodeRange.collapse(true);
24886      
24887         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24888         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24889          
24890         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24891         
24892         var nodeIsBefore   =  ss == 1;
24893         var nodeIsAfter    = ee == -1;
24894         
24895         if (nodeIsBefore && nodeIsAfter) {
24896             return 0; // outer
24897         }
24898         if (!nodeIsBefore && nodeIsAfter) {
24899             return 1; //right trailed.
24900         }
24901         
24902         if (nodeIsBefore && !nodeIsAfter) {
24903             return 2;  // left trailed.
24904         }
24905         // fully contined.
24906         return 3;
24907     },
24908
24909     // private? - in a new class?
24910     cleanUpPaste :  function()
24911     {
24912         // cleans up the whole document..
24913         Roo.log('cleanuppaste');
24914         
24915         this.cleanUpChildren(this.doc.body);
24916         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24917         if (clean != this.doc.body.innerHTML) {
24918             this.doc.body.innerHTML = clean;
24919         }
24920         
24921     },
24922     
24923     cleanWordChars : function(input) {// change the chars to hex code
24924         var he = Roo.HtmlEditorCore;
24925         
24926         var output = input;
24927         Roo.each(he.swapCodes, function(sw) { 
24928             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24929             
24930             output = output.replace(swapper, sw[1]);
24931         });
24932         
24933         return output;
24934     },
24935     
24936     
24937     cleanUpChildren : function (n)
24938     {
24939         if (!n.childNodes.length) {
24940             return;
24941         }
24942         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24943            this.cleanUpChild(n.childNodes[i]);
24944         }
24945     },
24946     
24947     
24948         
24949     
24950     cleanUpChild : function (node)
24951     {
24952         var ed = this;
24953         //console.log(node);
24954         if (node.nodeName == "#text") {
24955             // clean up silly Windows -- stuff?
24956             return; 
24957         }
24958         if (node.nodeName == "#comment") {
24959             node.parentNode.removeChild(node);
24960             // clean up silly Windows -- stuff?
24961             return; 
24962         }
24963         var lcname = node.tagName.toLowerCase();
24964         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24965         // whitelist of tags..
24966         
24967         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24968             // remove node.
24969             node.parentNode.removeChild(node);
24970             return;
24971             
24972         }
24973         
24974         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24975         
24976         // spans with no attributes - just remove them..
24977         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24978             remove_keep_children = true;
24979         }
24980         
24981         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24982         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24983         
24984         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24985         //    remove_keep_children = true;
24986         //}
24987         
24988         if (remove_keep_children) {
24989             this.cleanUpChildren(node);
24990             // inserts everything just before this node...
24991             while (node.childNodes.length) {
24992                 var cn = node.childNodes[0];
24993                 node.removeChild(cn);
24994                 node.parentNode.insertBefore(cn, node);
24995             }
24996             node.parentNode.removeChild(node);
24997             return;
24998         }
24999         
25000         if (!node.attributes || !node.attributes.length) {
25001             
25002           
25003             
25004             
25005             this.cleanUpChildren(node);
25006             return;
25007         }
25008         
25009         function cleanAttr(n,v)
25010         {
25011             
25012             if (v.match(/^\./) || v.match(/^\//)) {
25013                 return;
25014             }
25015             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25016                 return;
25017             }
25018             if (v.match(/^#/)) {
25019                 return;
25020             }
25021             if (v.match(/^\{/)) { // allow template editing.
25022                 return;
25023             }
25024 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25025             node.removeAttribute(n);
25026             
25027         }
25028         
25029         var cwhite = this.cwhite;
25030         var cblack = this.cblack;
25031             
25032         function cleanStyle(n,v)
25033         {
25034             if (v.match(/expression/)) { //XSS?? should we even bother..
25035                 node.removeAttribute(n);
25036                 return;
25037             }
25038             
25039             var parts = v.split(/;/);
25040             var clean = [];
25041             
25042             Roo.each(parts, function(p) {
25043                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25044                 if (!p.length) {
25045                     return true;
25046                 }
25047                 var l = p.split(':').shift().replace(/\s+/g,'');
25048                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25049                 
25050                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25051 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25052                     //node.removeAttribute(n);
25053                     return true;
25054                 }
25055                 //Roo.log()
25056                 // only allow 'c whitelisted system attributes'
25057                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25058 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25059                     //node.removeAttribute(n);
25060                     return true;
25061                 }
25062                 
25063                 
25064                  
25065                 
25066                 clean.push(p);
25067                 return true;
25068             });
25069             if (clean.length) { 
25070                 node.setAttribute(n, clean.join(';'));
25071             } else {
25072                 node.removeAttribute(n);
25073             }
25074             
25075         }
25076         
25077         
25078         for (var i = node.attributes.length-1; i > -1 ; i--) {
25079             var a = node.attributes[i];
25080             //console.log(a);
25081             
25082             if (a.name.toLowerCase().substr(0,2)=='on')  {
25083                 node.removeAttribute(a.name);
25084                 continue;
25085             }
25086             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25087                 node.removeAttribute(a.name);
25088                 continue;
25089             }
25090             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25091                 cleanAttr(a.name,a.value); // fixme..
25092                 continue;
25093             }
25094             if (a.name == 'style') {
25095                 cleanStyle(a.name,a.value);
25096                 continue;
25097             }
25098             /// clean up MS crap..
25099             // tecnically this should be a list of valid class'es..
25100             
25101             
25102             if (a.name == 'class') {
25103                 if (a.value.match(/^Mso/)) {
25104                     node.removeAttribute('class');
25105                 }
25106                 
25107                 if (a.value.match(/^body$/)) {
25108                     node.removeAttribute('class');
25109                 }
25110                 continue;
25111             }
25112             
25113             // style cleanup!?
25114             // class cleanup?
25115             
25116         }
25117         
25118         
25119         this.cleanUpChildren(node);
25120         
25121         
25122     },
25123     
25124     /**
25125      * Clean up MS wordisms...
25126      */
25127     cleanWord : function(node)
25128     {
25129         if (!node) {
25130             this.cleanWord(this.doc.body);
25131             return;
25132         }
25133         
25134         if(
25135                 node.nodeName == 'SPAN' &&
25136                 !node.hasAttributes() &&
25137                 node.childNodes.length == 1 &&
25138                 node.firstChild.nodeName == "#text"  
25139         ) {
25140             var textNode = node.firstChild;
25141             node.removeChild(textNode);
25142             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25143                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25144             }
25145             node.parentNode.insertBefore(textNode, node);
25146             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25147                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" ") , node);
25148             }
25149             node.parentNode.removeChild(node);
25150         }
25151         
25152         if (node.nodeName == "#text") {
25153             // clean up silly Windows -- stuff?
25154             return; 
25155         }
25156         if (node.nodeName == "#comment") {
25157             node.parentNode.removeChild(node);
25158             // clean up silly Windows -- stuff?
25159             return; 
25160         }
25161         
25162         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25163             node.parentNode.removeChild(node);
25164             return;
25165         }
25166         //Roo.log(node.tagName);
25167         // remove - but keep children..
25168         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25169             //Roo.log('-- removed');
25170             while (node.childNodes.length) {
25171                 var cn = node.childNodes[0];
25172                 node.removeChild(cn);
25173                 node.parentNode.insertBefore(cn, node);
25174                 // move node to parent - and clean it..
25175                 this.cleanWord(cn);
25176             }
25177             node.parentNode.removeChild(node);
25178             /// no need to iterate chidlren = it's got none..
25179             //this.iterateChildren(node, this.cleanWord);
25180             return;
25181         }
25182         // clean styles
25183         if (node.className.length) {
25184             
25185             var cn = node.className.split(/\W+/);
25186             var cna = [];
25187             Roo.each(cn, function(cls) {
25188                 if (cls.match(/Mso[a-zA-Z]+/)) {
25189                     return;
25190                 }
25191                 cna.push(cls);
25192             });
25193             node.className = cna.length ? cna.join(' ') : '';
25194             if (!cna.length) {
25195                 node.removeAttribute("class");
25196             }
25197         }
25198         
25199         if (node.hasAttribute("lang")) {
25200             node.removeAttribute("lang");
25201         }
25202         
25203         if (node.hasAttribute("style")) {
25204             
25205             var styles = node.getAttribute("style").split(";");
25206             var nstyle = [];
25207             Roo.each(styles, function(s) {
25208                 if (!s.match(/:/)) {
25209                     return;
25210                 }
25211                 var kv = s.split(":");
25212                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25213                     return;
25214                 }
25215                 // what ever is left... we allow.
25216                 nstyle.push(s);
25217             });
25218             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25219             if (!nstyle.length) {
25220                 node.removeAttribute('style');
25221             }
25222         }
25223         this.iterateChildren(node, this.cleanWord);
25224         
25225         
25226         
25227     },
25228     /**
25229      * iterateChildren of a Node, calling fn each time, using this as the scole..
25230      * @param {DomNode} node node to iterate children of.
25231      * @param {Function} fn method of this class to call on each item.
25232      */
25233     iterateChildren : function(node, fn)
25234     {
25235         if (!node.childNodes.length) {
25236                 return;
25237         }
25238         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25239            fn.call(this, node.childNodes[i])
25240         }
25241     },
25242     
25243     
25244     /**
25245      * cleanTableWidths.
25246      *
25247      * Quite often pasting from word etc.. results in tables with column and widths.
25248      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25249      *
25250      */
25251     cleanTableWidths : function(node)
25252     {
25253          
25254          
25255         if (!node) {
25256             this.cleanTableWidths(this.doc.body);
25257             return;
25258         }
25259         
25260         // ignore list...
25261         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25262             return; 
25263         }
25264         Roo.log(node.tagName);
25265         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25266             this.iterateChildren(node, this.cleanTableWidths);
25267             return;
25268         }
25269         if (node.hasAttribute('width')) {
25270             node.removeAttribute('width');
25271         }
25272         
25273          
25274         if (node.hasAttribute("style")) {
25275             // pretty basic...
25276             
25277             var styles = node.getAttribute("style").split(";");
25278             var nstyle = [];
25279             Roo.each(styles, function(s) {
25280                 if (!s.match(/:/)) {
25281                     return;
25282                 }
25283                 var kv = s.split(":");
25284                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25285                     return;
25286                 }
25287                 // what ever is left... we allow.
25288                 nstyle.push(s);
25289             });
25290             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25291             if (!nstyle.length) {
25292                 node.removeAttribute('style');
25293             }
25294         }
25295         
25296         this.iterateChildren(node, this.cleanTableWidths);
25297         
25298         
25299     },
25300     
25301     
25302     
25303     
25304     domToHTML : function(currentElement, depth, nopadtext) {
25305         
25306         depth = depth || 0;
25307         nopadtext = nopadtext || false;
25308     
25309         if (!currentElement) {
25310             return this.domToHTML(this.doc.body);
25311         }
25312         
25313         //Roo.log(currentElement);
25314         var j;
25315         var allText = false;
25316         var nodeName = currentElement.nodeName;
25317         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25318         
25319         if  (nodeName == '#text') {
25320             
25321             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25322         }
25323         
25324         
25325         var ret = '';
25326         if (nodeName != 'BODY') {
25327              
25328             var i = 0;
25329             // Prints the node tagName, such as <A>, <IMG>, etc
25330             if (tagName) {
25331                 var attr = [];
25332                 for(i = 0; i < currentElement.attributes.length;i++) {
25333                     // quoting?
25334                     var aname = currentElement.attributes.item(i).name;
25335                     if (!currentElement.attributes.item(i).value.length) {
25336                         continue;
25337                     }
25338                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25339                 }
25340                 
25341                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25342             } 
25343             else {
25344                 
25345                 // eack
25346             }
25347         } else {
25348             tagName = false;
25349         }
25350         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25351             return ret;
25352         }
25353         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25354             nopadtext = true;
25355         }
25356         
25357         
25358         // Traverse the tree
25359         i = 0;
25360         var currentElementChild = currentElement.childNodes.item(i);
25361         var allText = true;
25362         var innerHTML  = '';
25363         lastnode = '';
25364         while (currentElementChild) {
25365             // Formatting code (indent the tree so it looks nice on the screen)
25366             var nopad = nopadtext;
25367             if (lastnode == 'SPAN') {
25368                 nopad  = true;
25369             }
25370             // text
25371             if  (currentElementChild.nodeName == '#text') {
25372                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25373                 toadd = nopadtext ? toadd : toadd.trim();
25374                 if (!nopad && toadd.length > 80) {
25375                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25376                 }
25377                 innerHTML  += toadd;
25378                 
25379                 i++;
25380                 currentElementChild = currentElement.childNodes.item(i);
25381                 lastNode = '';
25382                 continue;
25383             }
25384             allText = false;
25385             
25386             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25387                 
25388             // Recursively traverse the tree structure of the child node
25389             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25390             lastnode = currentElementChild.nodeName;
25391             i++;
25392             currentElementChild=currentElement.childNodes.item(i);
25393         }
25394         
25395         ret += innerHTML;
25396         
25397         if (!allText) {
25398                 // The remaining code is mostly for formatting the tree
25399             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25400         }
25401         
25402         
25403         if (tagName) {
25404             ret+= "</"+tagName+">";
25405         }
25406         return ret;
25407         
25408     },
25409         
25410     applyBlacklists : function()
25411     {
25412         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25413         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25414         
25415         this.white = [];
25416         this.black = [];
25417         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25418             if (b.indexOf(tag) > -1) {
25419                 return;
25420             }
25421             this.white.push(tag);
25422             
25423         }, this);
25424         
25425         Roo.each(w, function(tag) {
25426             if (b.indexOf(tag) > -1) {
25427                 return;
25428             }
25429             if (this.white.indexOf(tag) > -1) {
25430                 return;
25431             }
25432             this.white.push(tag);
25433             
25434         }, this);
25435         
25436         
25437         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25438             if (w.indexOf(tag) > -1) {
25439                 return;
25440             }
25441             this.black.push(tag);
25442             
25443         }, this);
25444         
25445         Roo.each(b, function(tag) {
25446             if (w.indexOf(tag) > -1) {
25447                 return;
25448             }
25449             if (this.black.indexOf(tag) > -1) {
25450                 return;
25451             }
25452             this.black.push(tag);
25453             
25454         }, this);
25455         
25456         
25457         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25458         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25459         
25460         this.cwhite = [];
25461         this.cblack = [];
25462         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25463             if (b.indexOf(tag) > -1) {
25464                 return;
25465             }
25466             this.cwhite.push(tag);
25467             
25468         }, this);
25469         
25470         Roo.each(w, function(tag) {
25471             if (b.indexOf(tag) > -1) {
25472                 return;
25473             }
25474             if (this.cwhite.indexOf(tag) > -1) {
25475                 return;
25476             }
25477             this.cwhite.push(tag);
25478             
25479         }, this);
25480         
25481         
25482         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25483             if (w.indexOf(tag) > -1) {
25484                 return;
25485             }
25486             this.cblack.push(tag);
25487             
25488         }, this);
25489         
25490         Roo.each(b, function(tag) {
25491             if (w.indexOf(tag) > -1) {
25492                 return;
25493             }
25494             if (this.cblack.indexOf(tag) > -1) {
25495                 return;
25496             }
25497             this.cblack.push(tag);
25498             
25499         }, this);
25500     },
25501     
25502     setStylesheets : function(stylesheets)
25503     {
25504         if(typeof(stylesheets) == 'string'){
25505             Roo.get(this.iframe.contentDocument.head).createChild({
25506                 tag : 'link',
25507                 rel : 'stylesheet',
25508                 type : 'text/css',
25509                 href : stylesheets
25510             });
25511             
25512             return;
25513         }
25514         var _this = this;
25515      
25516         Roo.each(stylesheets, function(s) {
25517             if(!s.length){
25518                 return;
25519             }
25520             
25521             Roo.get(_this.iframe.contentDocument.head).createChild({
25522                 tag : 'link',
25523                 rel : 'stylesheet',
25524                 type : 'text/css',
25525                 href : s
25526             });
25527         });
25528
25529         
25530     },
25531     
25532     removeStylesheets : function()
25533     {
25534         var _this = this;
25535         
25536         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25537             s.remove();
25538         });
25539     },
25540     
25541     setStyle : function(style)
25542     {
25543         Roo.get(this.iframe.contentDocument.head).createChild({
25544             tag : 'style',
25545             type : 'text/css',
25546             html : style
25547         });
25548
25549         return;
25550     }
25551     
25552     // hide stuff that is not compatible
25553     /**
25554      * @event blur
25555      * @hide
25556      */
25557     /**
25558      * @event change
25559      * @hide
25560      */
25561     /**
25562      * @event focus
25563      * @hide
25564      */
25565     /**
25566      * @event specialkey
25567      * @hide
25568      */
25569     /**
25570      * @cfg {String} fieldClass @hide
25571      */
25572     /**
25573      * @cfg {String} focusClass @hide
25574      */
25575     /**
25576      * @cfg {String} autoCreate @hide
25577      */
25578     /**
25579      * @cfg {String} inputType @hide
25580      */
25581     /**
25582      * @cfg {String} invalidClass @hide
25583      */
25584     /**
25585      * @cfg {String} invalidText @hide
25586      */
25587     /**
25588      * @cfg {String} msgFx @hide
25589      */
25590     /**
25591      * @cfg {String} validateOnBlur @hide
25592      */
25593 });
25594
25595 Roo.HtmlEditorCore.white = [
25596         'area', 'br', 'img', 'input', 'hr', 'wbr',
25597         
25598        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25599        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25600        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25601        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25602        'table',   'ul',         'xmp', 
25603        
25604        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25605       'thead',   'tr', 
25606      
25607       'dir', 'menu', 'ol', 'ul', 'dl',
25608        
25609       'embed',  'object'
25610 ];
25611
25612
25613 Roo.HtmlEditorCore.black = [
25614     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25615         'applet', // 
25616         'base',   'basefont', 'bgsound', 'blink',  'body', 
25617         'frame',  'frameset', 'head',    'html',   'ilayer', 
25618         'iframe', 'layer',  'link',     'meta',    'object',   
25619         'script', 'style' ,'title',  'xml' // clean later..
25620 ];
25621 Roo.HtmlEditorCore.clean = [
25622     'script', 'style', 'title', 'xml'
25623 ];
25624 Roo.HtmlEditorCore.remove = [
25625     'font'
25626 ];
25627 // attributes..
25628
25629 Roo.HtmlEditorCore.ablack = [
25630     'on'
25631 ];
25632     
25633 Roo.HtmlEditorCore.aclean = [ 
25634     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25635 ];
25636
25637 // protocols..
25638 Roo.HtmlEditorCore.pwhite= [
25639         'http',  'https',  'mailto'
25640 ];
25641
25642 // white listed style attributes.
25643 Roo.HtmlEditorCore.cwhite= [
25644       //  'text-align', /// default is to allow most things..
25645       
25646          
25647 //        'font-size'//??
25648 ];
25649
25650 // black listed style attributes.
25651 Roo.HtmlEditorCore.cblack= [
25652       //  'font-size' -- this can be set by the project 
25653 ];
25654
25655
25656 Roo.HtmlEditorCore.swapCodes   =[ 
25657     [    8211, "--" ], 
25658     [    8212, "--" ], 
25659     [    8216,  "'" ],  
25660     [    8217, "'" ],  
25661     [    8220, '"' ],  
25662     [    8221, '"' ],  
25663     [    8226, "*" ],  
25664     [    8230, "..." ]
25665 ]; 
25666
25667     /*
25668  * - LGPL
25669  *
25670  * HtmlEditor
25671  * 
25672  */
25673
25674 /**
25675  * @class Roo.bootstrap.HtmlEditor
25676  * @extends Roo.bootstrap.TextArea
25677  * Bootstrap HtmlEditor class
25678
25679  * @constructor
25680  * Create a new HtmlEditor
25681  * @param {Object} config The config object
25682  */
25683
25684 Roo.bootstrap.HtmlEditor = function(config){
25685     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25686     if (!this.toolbars) {
25687         this.toolbars = [];
25688     }
25689     
25690     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25691     this.addEvents({
25692             /**
25693              * @event initialize
25694              * Fires when the editor is fully initialized (including the iframe)
25695              * @param {HtmlEditor} this
25696              */
25697             initialize: true,
25698             /**
25699              * @event activate
25700              * Fires when the editor is first receives the focus. Any insertion must wait
25701              * until after this event.
25702              * @param {HtmlEditor} this
25703              */
25704             activate: true,
25705              /**
25706              * @event beforesync
25707              * Fires before the textarea is updated with content from the editor iframe. Return false
25708              * to cancel the sync.
25709              * @param {HtmlEditor} this
25710              * @param {String} html
25711              */
25712             beforesync: true,
25713              /**
25714              * @event beforepush
25715              * Fires before the iframe editor is updated with content from the textarea. Return false
25716              * to cancel the push.
25717              * @param {HtmlEditor} this
25718              * @param {String} html
25719              */
25720             beforepush: true,
25721              /**
25722              * @event sync
25723              * Fires when the textarea is updated with content from the editor iframe.
25724              * @param {HtmlEditor} this
25725              * @param {String} html
25726              */
25727             sync: true,
25728              /**
25729              * @event push
25730              * Fires when the iframe editor is updated with content from the textarea.
25731              * @param {HtmlEditor} this
25732              * @param {String} html
25733              */
25734             push: true,
25735              /**
25736              * @event editmodechange
25737              * Fires when the editor switches edit modes
25738              * @param {HtmlEditor} this
25739              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25740              */
25741             editmodechange: true,
25742             /**
25743              * @event editorevent
25744              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25745              * @param {HtmlEditor} this
25746              */
25747             editorevent: true,
25748             /**
25749              * @event firstfocus
25750              * Fires when on first focus - needed by toolbars..
25751              * @param {HtmlEditor} this
25752              */
25753             firstfocus: true,
25754             /**
25755              * @event autosave
25756              * Auto save the htmlEditor value as a file into Events
25757              * @param {HtmlEditor} this
25758              */
25759             autosave: true,
25760             /**
25761              * @event savedpreview
25762              * preview the saved version of htmlEditor
25763              * @param {HtmlEditor} this
25764              */
25765             savedpreview: true
25766         });
25767 };
25768
25769
25770 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25771     
25772     
25773       /**
25774      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25775      */
25776     toolbars : false,
25777     
25778      /**
25779     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25780     */
25781     btns : [],
25782    
25783      /**
25784      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25785      *                        Roo.resizable.
25786      */
25787     resizable : false,
25788      /**
25789      * @cfg {Number} height (in pixels)
25790      */   
25791     height: 300,
25792    /**
25793      * @cfg {Number} width (in pixels)
25794      */   
25795     width: false,
25796     
25797     /**
25798      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25799      * 
25800      */
25801     stylesheets: false,
25802     
25803     // id of frame..
25804     frameId: false,
25805     
25806     // private properties
25807     validationEvent : false,
25808     deferHeight: true,
25809     initialized : false,
25810     activated : false,
25811     
25812     onFocus : Roo.emptyFn,
25813     iframePad:3,
25814     hideMode:'offsets',
25815     
25816     tbContainer : false,
25817     
25818     bodyCls : '',
25819     
25820     toolbarContainer :function() {
25821         return this.wrap.select('.x-html-editor-tb',true).first();
25822     },
25823
25824     /**
25825      * Protected method that will not generally be called directly. It
25826      * is called when the editor creates its toolbar. Override this method if you need to
25827      * add custom toolbar buttons.
25828      * @param {HtmlEditor} editor
25829      */
25830     createToolbar : function(){
25831         Roo.log('renewing');
25832         Roo.log("create toolbars");
25833         
25834         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25835         this.toolbars[0].render(this.toolbarContainer());
25836         
25837         return;
25838         
25839 //        if (!editor.toolbars || !editor.toolbars.length) {
25840 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25841 //        }
25842 //        
25843 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25844 //            editor.toolbars[i] = Roo.factory(
25845 //                    typeof(editor.toolbars[i]) == 'string' ?
25846 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25847 //                Roo.bootstrap.HtmlEditor);
25848 //            editor.toolbars[i].init(editor);
25849 //        }
25850     },
25851
25852      
25853     // private
25854     onRender : function(ct, position)
25855     {
25856        // Roo.log("Call onRender: " + this.xtype);
25857         var _t = this;
25858         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25859       
25860         this.wrap = this.inputEl().wrap({
25861             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25862         });
25863         
25864         this.editorcore.onRender(ct, position);
25865          
25866         if (this.resizable) {
25867             this.resizeEl = new Roo.Resizable(this.wrap, {
25868                 pinned : true,
25869                 wrap: true,
25870                 dynamic : true,
25871                 minHeight : this.height,
25872                 height: this.height,
25873                 handles : this.resizable,
25874                 width: this.width,
25875                 listeners : {
25876                     resize : function(r, w, h) {
25877                         _t.onResize(w,h); // -something
25878                     }
25879                 }
25880             });
25881             
25882         }
25883         this.createToolbar(this);
25884        
25885         
25886         if(!this.width && this.resizable){
25887             this.setSize(this.wrap.getSize());
25888         }
25889         if (this.resizeEl) {
25890             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25891             // should trigger onReize..
25892         }
25893         
25894     },
25895
25896     // private
25897     onResize : function(w, h)
25898     {
25899         Roo.log('resize: ' +w + ',' + h );
25900         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25901         var ew = false;
25902         var eh = false;
25903         
25904         if(this.inputEl() ){
25905             if(typeof w == 'number'){
25906                 var aw = w - this.wrap.getFrameWidth('lr');
25907                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25908                 ew = aw;
25909             }
25910             if(typeof h == 'number'){
25911                  var tbh = -11;  // fixme it needs to tool bar size!
25912                 for (var i =0; i < this.toolbars.length;i++) {
25913                     // fixme - ask toolbars for heights?
25914                     tbh += this.toolbars[i].el.getHeight();
25915                     //if (this.toolbars[i].footer) {
25916                     //    tbh += this.toolbars[i].footer.el.getHeight();
25917                     //}
25918                 }
25919               
25920                 
25921                 
25922                 
25923                 
25924                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25925                 ah -= 5; // knock a few pixes off for look..
25926                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25927                 var eh = ah;
25928             }
25929         }
25930         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25931         this.editorcore.onResize(ew,eh);
25932         
25933     },
25934
25935     /**
25936      * Toggles the editor between standard and source edit mode.
25937      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25938      */
25939     toggleSourceEdit : function(sourceEditMode)
25940     {
25941         this.editorcore.toggleSourceEdit(sourceEditMode);
25942         
25943         if(this.editorcore.sourceEditMode){
25944             Roo.log('editor - showing textarea');
25945             
25946 //            Roo.log('in');
25947 //            Roo.log(this.syncValue());
25948             this.syncValue();
25949             this.inputEl().removeClass(['hide', 'x-hidden']);
25950             this.inputEl().dom.removeAttribute('tabIndex');
25951             this.inputEl().focus();
25952         }else{
25953             Roo.log('editor - hiding textarea');
25954 //            Roo.log('out')
25955 //            Roo.log(this.pushValue()); 
25956             this.pushValue();
25957             
25958             this.inputEl().addClass(['hide', 'x-hidden']);
25959             this.inputEl().dom.setAttribute('tabIndex', -1);
25960             //this.deferFocus();
25961         }
25962          
25963         if(this.resizable){
25964             this.setSize(this.wrap.getSize());
25965         }
25966         
25967         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25968     },
25969  
25970     // private (for BoxComponent)
25971     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25972
25973     // private (for BoxComponent)
25974     getResizeEl : function(){
25975         return this.wrap;
25976     },
25977
25978     // private (for BoxComponent)
25979     getPositionEl : function(){
25980         return this.wrap;
25981     },
25982
25983     // private
25984     initEvents : function(){
25985         this.originalValue = this.getValue();
25986     },
25987
25988 //    /**
25989 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25990 //     * @method
25991 //     */
25992 //    markInvalid : Roo.emptyFn,
25993 //    /**
25994 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25995 //     * @method
25996 //     */
25997 //    clearInvalid : Roo.emptyFn,
25998
25999     setValue : function(v){
26000         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
26001         this.editorcore.pushValue();
26002     },
26003
26004      
26005     // private
26006     deferFocus : function(){
26007         this.focus.defer(10, this);
26008     },
26009
26010     // doc'ed in Field
26011     focus : function(){
26012         this.editorcore.focus();
26013         
26014     },
26015       
26016
26017     // private
26018     onDestroy : function(){
26019         
26020         
26021         
26022         if(this.rendered){
26023             
26024             for (var i =0; i < this.toolbars.length;i++) {
26025                 // fixme - ask toolbars for heights?
26026                 this.toolbars[i].onDestroy();
26027             }
26028             
26029             this.wrap.dom.innerHTML = '';
26030             this.wrap.remove();
26031         }
26032     },
26033
26034     // private
26035     onFirstFocus : function(){
26036         //Roo.log("onFirstFocus");
26037         this.editorcore.onFirstFocus();
26038          for (var i =0; i < this.toolbars.length;i++) {
26039             this.toolbars[i].onFirstFocus();
26040         }
26041         
26042     },
26043     
26044     // private
26045     syncValue : function()
26046     {   
26047         this.editorcore.syncValue();
26048     },
26049     
26050     pushValue : function()
26051     {   
26052         this.editorcore.pushValue();
26053     }
26054      
26055     
26056     // hide stuff that is not compatible
26057     /**
26058      * @event blur
26059      * @hide
26060      */
26061     /**
26062      * @event change
26063      * @hide
26064      */
26065     /**
26066      * @event focus
26067      * @hide
26068      */
26069     /**
26070      * @event specialkey
26071      * @hide
26072      */
26073     /**
26074      * @cfg {String} fieldClass @hide
26075      */
26076     /**
26077      * @cfg {String} focusClass @hide
26078      */
26079     /**
26080      * @cfg {String} autoCreate @hide
26081      */
26082     /**
26083      * @cfg {String} inputType @hide
26084      */
26085      
26086     /**
26087      * @cfg {String} invalidText @hide
26088      */
26089     /**
26090      * @cfg {String} msgFx @hide
26091      */
26092     /**
26093      * @cfg {String} validateOnBlur @hide
26094      */
26095 });
26096  
26097     
26098    
26099    
26100    
26101       
26102 Roo.namespace('Roo.bootstrap.htmleditor');
26103 /**
26104  * @class Roo.bootstrap.HtmlEditorToolbar1
26105  * Basic Toolbar
26106  * 
26107  * @example
26108  * Usage:
26109  *
26110  new Roo.bootstrap.HtmlEditor({
26111     ....
26112     toolbars : [
26113         new Roo.bootstrap.HtmlEditorToolbar1({
26114             disable : { fonts: 1 , format: 1, ..., ... , ...],
26115             btns : [ .... ]
26116         })
26117     }
26118      
26119  * 
26120  * @cfg {Object} disable List of elements to disable..
26121  * @cfg {Array} btns List of additional buttons.
26122  * 
26123  * 
26124  * NEEDS Extra CSS? 
26125  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26126  */
26127  
26128 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26129 {
26130     
26131     Roo.apply(this, config);
26132     
26133     // default disabled, based on 'good practice'..
26134     this.disable = this.disable || {};
26135     Roo.applyIf(this.disable, {
26136         fontSize : true,
26137         colors : true,
26138         specialElements : true
26139     });
26140     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26141     
26142     this.editor = config.editor;
26143     this.editorcore = config.editor.editorcore;
26144     
26145     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26146     
26147     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26148     // dont call parent... till later.
26149 }
26150 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26151      
26152     bar : true,
26153     
26154     editor : false,
26155     editorcore : false,
26156     
26157     
26158     formats : [
26159         "p" ,  
26160         "h1","h2","h3","h4","h5","h6", 
26161         "pre", "code", 
26162         "abbr", "acronym", "address", "cite", "samp", "var",
26163         'div','span'
26164     ],
26165     
26166     onRender : function(ct, position)
26167     {
26168        // Roo.log("Call onRender: " + this.xtype);
26169         
26170        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26171        Roo.log(this.el);
26172        this.el.dom.style.marginBottom = '0';
26173        var _this = this;
26174        var editorcore = this.editorcore;
26175        var editor= this.editor;
26176        
26177        var children = [];
26178        var btn = function(id,cmd , toggle, handler, html){
26179        
26180             var  event = toggle ? 'toggle' : 'click';
26181        
26182             var a = {
26183                 size : 'sm',
26184                 xtype: 'Button',
26185                 xns: Roo.bootstrap,
26186                 //glyphicon : id,
26187                 fa: id,
26188                 cmd : id || cmd,
26189                 enableToggle:toggle !== false,
26190                 html : html || '',
26191                 pressed : toggle ? false : null,
26192                 listeners : {}
26193             };
26194             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26195                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26196             };
26197             children.push(a);
26198             return a;
26199        }
26200        
26201     //    var cb_box = function...
26202         
26203         var style = {
26204                 xtype: 'Button',
26205                 size : 'sm',
26206                 xns: Roo.bootstrap,
26207                 fa : 'font',
26208                 //html : 'submit'
26209                 menu : {
26210                     xtype: 'Menu',
26211                     xns: Roo.bootstrap,
26212                     items:  []
26213                 }
26214         };
26215         Roo.each(this.formats, function(f) {
26216             style.menu.items.push({
26217                 xtype :'MenuItem',
26218                 xns: Roo.bootstrap,
26219                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26220                 tagname : f,
26221                 listeners : {
26222                     click : function()
26223                     {
26224                         editorcore.insertTag(this.tagname);
26225                         editor.focus();
26226                     }
26227                 }
26228                 
26229             });
26230         });
26231         children.push(style);   
26232         
26233         btn('bold',false,true);
26234         btn('italic',false,true);
26235         btn('align-left', 'justifyleft',true);
26236         btn('align-center', 'justifycenter',true);
26237         btn('align-right' , 'justifyright',true);
26238         btn('link', false, false, function(btn) {
26239             //Roo.log("create link?");
26240             var url = prompt(this.createLinkText, this.defaultLinkValue);
26241             if(url && url != 'http:/'+'/'){
26242                 this.editorcore.relayCmd('createlink', url);
26243             }
26244         }),
26245         btn('list','insertunorderedlist',true);
26246         btn('pencil', false,true, function(btn){
26247                 Roo.log(this);
26248                 this.toggleSourceEdit(btn.pressed);
26249         });
26250         
26251         if (this.editor.btns.length > 0) {
26252             for (var i = 0; i<this.editor.btns.length; i++) {
26253                 children.push(this.editor.btns[i]);
26254             }
26255         }
26256         
26257         /*
26258         var cog = {
26259                 xtype: 'Button',
26260                 size : 'sm',
26261                 xns: Roo.bootstrap,
26262                 glyphicon : 'cog',
26263                 //html : 'submit'
26264                 menu : {
26265                     xtype: 'Menu',
26266                     xns: Roo.bootstrap,
26267                     items:  []
26268                 }
26269         };
26270         
26271         cog.menu.items.push({
26272             xtype :'MenuItem',
26273             xns: Roo.bootstrap,
26274             html : Clean styles,
26275             tagname : f,
26276             listeners : {
26277                 click : function()
26278                 {
26279                     editorcore.insertTag(this.tagname);
26280                     editor.focus();
26281                 }
26282             }
26283             
26284         });
26285        */
26286         
26287          
26288        this.xtype = 'NavSimplebar';
26289         
26290         for(var i=0;i< children.length;i++) {
26291             
26292             this.buttons.add(this.addxtypeChild(children[i]));
26293             
26294         }
26295         
26296         editor.on('editorevent', this.updateToolbar, this);
26297     },
26298     onBtnClick : function(id)
26299     {
26300        this.editorcore.relayCmd(id);
26301        this.editorcore.focus();
26302     },
26303     
26304     /**
26305      * Protected method that will not generally be called directly. It triggers
26306      * a toolbar update by reading the markup state of the current selection in the editor.
26307      */
26308     updateToolbar: function(){
26309
26310         if(!this.editorcore.activated){
26311             this.editor.onFirstFocus(); // is this neeed?
26312             return;
26313         }
26314
26315         var btns = this.buttons; 
26316         var doc = this.editorcore.doc;
26317         btns.get('bold').setActive(doc.queryCommandState('bold'));
26318         btns.get('italic').setActive(doc.queryCommandState('italic'));
26319         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26320         
26321         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26322         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26323         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26324         
26325         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26326         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26327          /*
26328         
26329         var ans = this.editorcore.getAllAncestors();
26330         if (this.formatCombo) {
26331             
26332             
26333             var store = this.formatCombo.store;
26334             this.formatCombo.setValue("");
26335             for (var i =0; i < ans.length;i++) {
26336                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26337                     // select it..
26338                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26339                     break;
26340                 }
26341             }
26342         }
26343         
26344         
26345         
26346         // hides menus... - so this cant be on a menu...
26347         Roo.bootstrap.MenuMgr.hideAll();
26348         */
26349         Roo.bootstrap.MenuMgr.hideAll();
26350         //this.editorsyncValue();
26351     },
26352     onFirstFocus: function() {
26353         this.buttons.each(function(item){
26354            item.enable();
26355         });
26356     },
26357     toggleSourceEdit : function(sourceEditMode){
26358         
26359           
26360         if(sourceEditMode){
26361             Roo.log("disabling buttons");
26362            this.buttons.each( function(item){
26363                 if(item.cmd != 'pencil'){
26364                     item.disable();
26365                 }
26366             });
26367           
26368         }else{
26369             Roo.log("enabling buttons");
26370             if(this.editorcore.initialized){
26371                 this.buttons.each( function(item){
26372                     item.enable();
26373                 });
26374             }
26375             
26376         }
26377         Roo.log("calling toggole on editor");
26378         // tell the editor that it's been pressed..
26379         this.editor.toggleSourceEdit(sourceEditMode);
26380        
26381     }
26382 });
26383
26384
26385
26386
26387  
26388 /*
26389  * - LGPL
26390  */
26391
26392 /**
26393  * @class Roo.bootstrap.Markdown
26394  * @extends Roo.bootstrap.TextArea
26395  * Bootstrap Showdown editable area
26396  * @cfg {string} content
26397  * 
26398  * @constructor
26399  * Create a new Showdown
26400  */
26401
26402 Roo.bootstrap.Markdown = function(config){
26403     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26404    
26405 };
26406
26407 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26408     
26409     editing :false,
26410     
26411     initEvents : function()
26412     {
26413         
26414         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26415         this.markdownEl = this.el.createChild({
26416             cls : 'roo-markdown-area'
26417         });
26418         this.inputEl().addClass('d-none');
26419         if (this.getValue() == '') {
26420             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26421             
26422         } else {
26423             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26424         }
26425         this.markdownEl.on('click', this.toggleTextEdit, this);
26426         this.on('blur', this.toggleTextEdit, this);
26427         this.on('specialkey', this.resizeTextArea, this);
26428     },
26429     
26430     toggleTextEdit : function()
26431     {
26432         var sh = this.markdownEl.getHeight();
26433         this.inputEl().addClass('d-none');
26434         this.markdownEl.addClass('d-none');
26435         if (!this.editing) {
26436             // show editor?
26437             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26438             this.inputEl().removeClass('d-none');
26439             this.inputEl().focus();
26440             this.editing = true;
26441             return;
26442         }
26443         // show showdown...
26444         this.updateMarkdown();
26445         this.markdownEl.removeClass('d-none');
26446         this.editing = false;
26447         return;
26448     },
26449     updateMarkdown : function()
26450     {
26451         if (this.getValue() == '') {
26452             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26453             return;
26454         }
26455  
26456         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26457     },
26458     
26459     resizeTextArea: function () {
26460         
26461         var sh = 100;
26462         Roo.log([sh, this.getValue().split("\n").length * 30]);
26463         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26464     },
26465     setValue : function(val)
26466     {
26467         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26468         if (!this.editing) {
26469             this.updateMarkdown();
26470         }
26471         
26472     },
26473     focus : function()
26474     {
26475         if (!this.editing) {
26476             this.toggleTextEdit();
26477         }
26478         
26479     }
26480
26481
26482 });
26483 /**
26484  * @class Roo.bootstrap.Table.AbstractSelectionModel
26485  * @extends Roo.util.Observable
26486  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26487  * implemented by descendant classes.  This class should not be directly instantiated.
26488  * @constructor
26489  */
26490 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26491     this.locked = false;
26492     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26493 };
26494
26495
26496 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26497     /** @ignore Called by the grid automatically. Do not call directly. */
26498     init : function(grid){
26499         this.grid = grid;
26500         this.initEvents();
26501     },
26502
26503     /**
26504      * Locks the selections.
26505      */
26506     lock : function(){
26507         this.locked = true;
26508     },
26509
26510     /**
26511      * Unlocks the selections.
26512      */
26513     unlock : function(){
26514         this.locked = false;
26515     },
26516
26517     /**
26518      * Returns true if the selections are locked.
26519      * @return {Boolean}
26520      */
26521     isLocked : function(){
26522         return this.locked;
26523     },
26524     
26525     
26526     initEvents : function ()
26527     {
26528         
26529     }
26530 });
26531 /**
26532  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26533  * @class Roo.bootstrap.Table.RowSelectionModel
26534  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26535  * It supports multiple selections and keyboard selection/navigation. 
26536  * @constructor
26537  * @param {Object} config
26538  */
26539
26540 Roo.bootstrap.Table.RowSelectionModel = function(config){
26541     Roo.apply(this, config);
26542     this.selections = new Roo.util.MixedCollection(false, function(o){
26543         return o.id;
26544     });
26545
26546     this.last = false;
26547     this.lastActive = false;
26548
26549     this.addEvents({
26550         /**
26551              * @event selectionchange
26552              * Fires when the selection changes
26553              * @param {SelectionModel} this
26554              */
26555             "selectionchange" : true,
26556         /**
26557              * @event afterselectionchange
26558              * Fires after the selection changes (eg. by key press or clicking)
26559              * @param {SelectionModel} this
26560              */
26561             "afterselectionchange" : true,
26562         /**
26563              * @event beforerowselect
26564              * Fires when a row is selected being selected, return false to cancel.
26565              * @param {SelectionModel} this
26566              * @param {Number} rowIndex The selected index
26567              * @param {Boolean} keepExisting False if other selections will be cleared
26568              */
26569             "beforerowselect" : true,
26570         /**
26571              * @event rowselect
26572              * Fires when a row is selected.
26573              * @param {SelectionModel} this
26574              * @param {Number} rowIndex The selected index
26575              * @param {Roo.data.Record} r The record
26576              */
26577             "rowselect" : true,
26578         /**
26579              * @event rowdeselect
26580              * Fires when a row is deselected.
26581              * @param {SelectionModel} this
26582              * @param {Number} rowIndex The selected index
26583              */
26584         "rowdeselect" : true
26585     });
26586     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26587     this.locked = false;
26588  };
26589
26590 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26591     /**
26592      * @cfg {Boolean} singleSelect
26593      * True to allow selection of only one row at a time (defaults to false)
26594      */
26595     singleSelect : false,
26596
26597     // private
26598     initEvents : function()
26599     {
26600
26601         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26602         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26603         //}else{ // allow click to work like normal
26604          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26605         //}
26606         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26607         this.grid.on("rowclick", this.handleMouseDown, this);
26608         
26609         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26610             "up" : function(e){
26611                 if(!e.shiftKey){
26612                     this.selectPrevious(e.shiftKey);
26613                 }else if(this.last !== false && this.lastActive !== false){
26614                     var last = this.last;
26615                     this.selectRange(this.last,  this.lastActive-1);
26616                     this.grid.getView().focusRow(this.lastActive);
26617                     if(last !== false){
26618                         this.last = last;
26619                     }
26620                 }else{
26621                     this.selectFirstRow();
26622                 }
26623                 this.fireEvent("afterselectionchange", this);
26624             },
26625             "down" : function(e){
26626                 if(!e.shiftKey){
26627                     this.selectNext(e.shiftKey);
26628                 }else if(this.last !== false && this.lastActive !== false){
26629                     var last = this.last;
26630                     this.selectRange(this.last,  this.lastActive+1);
26631                     this.grid.getView().focusRow(this.lastActive);
26632                     if(last !== false){
26633                         this.last = last;
26634                     }
26635                 }else{
26636                     this.selectFirstRow();
26637                 }
26638                 this.fireEvent("afterselectionchange", this);
26639             },
26640             scope: this
26641         });
26642         this.grid.store.on('load', function(){
26643             this.selections.clear();
26644         },this);
26645         /*
26646         var view = this.grid.view;
26647         view.on("refresh", this.onRefresh, this);
26648         view.on("rowupdated", this.onRowUpdated, this);
26649         view.on("rowremoved", this.onRemove, this);
26650         */
26651     },
26652
26653     // private
26654     onRefresh : function()
26655     {
26656         var ds = this.grid.store, i, v = this.grid.view;
26657         var s = this.selections;
26658         s.each(function(r){
26659             if((i = ds.indexOfId(r.id)) != -1){
26660                 v.onRowSelect(i);
26661             }else{
26662                 s.remove(r);
26663             }
26664         });
26665     },
26666
26667     // private
26668     onRemove : function(v, index, r){
26669         this.selections.remove(r);
26670     },
26671
26672     // private
26673     onRowUpdated : function(v, index, r){
26674         if(this.isSelected(r)){
26675             v.onRowSelect(index);
26676         }
26677     },
26678
26679     /**
26680      * Select records.
26681      * @param {Array} records The records to select
26682      * @param {Boolean} keepExisting (optional) True to keep existing selections
26683      */
26684     selectRecords : function(records, keepExisting)
26685     {
26686         if(!keepExisting){
26687             this.clearSelections();
26688         }
26689             var ds = this.grid.store;
26690         for(var i = 0, len = records.length; i < len; i++){
26691             this.selectRow(ds.indexOf(records[i]), true);
26692         }
26693     },
26694
26695     /**
26696      * Gets the number of selected rows.
26697      * @return {Number}
26698      */
26699     getCount : function(){
26700         return this.selections.length;
26701     },
26702
26703     /**
26704      * Selects the first row in the grid.
26705      */
26706     selectFirstRow : function(){
26707         this.selectRow(0);
26708     },
26709
26710     /**
26711      * Select the last row.
26712      * @param {Boolean} keepExisting (optional) True to keep existing selections
26713      */
26714     selectLastRow : function(keepExisting){
26715         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26716         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26717     },
26718
26719     /**
26720      * Selects the row immediately following the last selected row.
26721      * @param {Boolean} keepExisting (optional) True to keep existing selections
26722      */
26723     selectNext : function(keepExisting)
26724     {
26725             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26726             this.selectRow(this.last+1, keepExisting);
26727             this.grid.getView().focusRow(this.last);
26728         }
26729     },
26730
26731     /**
26732      * Selects the row that precedes the last selected row.
26733      * @param {Boolean} keepExisting (optional) True to keep existing selections
26734      */
26735     selectPrevious : function(keepExisting){
26736         if(this.last){
26737             this.selectRow(this.last-1, keepExisting);
26738             this.grid.getView().focusRow(this.last);
26739         }
26740     },
26741
26742     /**
26743      * Returns the selected records
26744      * @return {Array} Array of selected records
26745      */
26746     getSelections : function(){
26747         return [].concat(this.selections.items);
26748     },
26749
26750     /**
26751      * Returns the first selected record.
26752      * @return {Record}
26753      */
26754     getSelected : function(){
26755         return this.selections.itemAt(0);
26756     },
26757
26758
26759     /**
26760      * Clears all selections.
26761      */
26762     clearSelections : function(fast)
26763     {
26764         if(this.locked) {
26765             return;
26766         }
26767         if(fast !== true){
26768                 var ds = this.grid.store;
26769             var s = this.selections;
26770             s.each(function(r){
26771                 this.deselectRow(ds.indexOfId(r.id));
26772             }, this);
26773             s.clear();
26774         }else{
26775             this.selections.clear();
26776         }
26777         this.last = false;
26778     },
26779
26780
26781     /**
26782      * Selects all rows.
26783      */
26784     selectAll : function(){
26785         if(this.locked) {
26786             return;
26787         }
26788         this.selections.clear();
26789         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26790             this.selectRow(i, true);
26791         }
26792     },
26793
26794     /**
26795      * Returns True if there is a selection.
26796      * @return {Boolean}
26797      */
26798     hasSelection : function(){
26799         return this.selections.length > 0;
26800     },
26801
26802     /**
26803      * Returns True if the specified row is selected.
26804      * @param {Number/Record} record The record or index of the record to check
26805      * @return {Boolean}
26806      */
26807     isSelected : function(index){
26808             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26809         return (r && this.selections.key(r.id) ? true : false);
26810     },
26811
26812     /**
26813      * Returns True if the specified record id is selected.
26814      * @param {String} id The id of record to check
26815      * @return {Boolean}
26816      */
26817     isIdSelected : function(id){
26818         return (this.selections.key(id) ? true : false);
26819     },
26820
26821
26822     // private
26823     handleMouseDBClick : function(e, t){
26824         
26825     },
26826     // private
26827     handleMouseDown : function(e, t)
26828     {
26829             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26830         if(this.isLocked() || rowIndex < 0 ){
26831             return;
26832         };
26833         if(e.shiftKey && this.last !== false){
26834             var last = this.last;
26835             this.selectRange(last, rowIndex, e.ctrlKey);
26836             this.last = last; // reset the last
26837             t.focus();
26838     
26839         }else{
26840             var isSelected = this.isSelected(rowIndex);
26841             //Roo.log("select row:" + rowIndex);
26842             if(isSelected){
26843                 this.deselectRow(rowIndex);
26844             } else {
26845                         this.selectRow(rowIndex, true);
26846             }
26847     
26848             /*
26849                 if(e.button !== 0 && isSelected){
26850                 alert('rowIndex 2: ' + rowIndex);
26851                     view.focusRow(rowIndex);
26852                 }else if(e.ctrlKey && isSelected){
26853                     this.deselectRow(rowIndex);
26854                 }else if(!isSelected){
26855                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26856                     view.focusRow(rowIndex);
26857                 }
26858             */
26859         }
26860         this.fireEvent("afterselectionchange", this);
26861     },
26862     // private
26863     handleDragableRowClick :  function(grid, rowIndex, e) 
26864     {
26865         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26866             this.selectRow(rowIndex, false);
26867             grid.view.focusRow(rowIndex);
26868              this.fireEvent("afterselectionchange", this);
26869         }
26870     },
26871     
26872     /**
26873      * Selects multiple rows.
26874      * @param {Array} rows Array of the indexes of the row to select
26875      * @param {Boolean} keepExisting (optional) True to keep existing selections
26876      */
26877     selectRows : function(rows, keepExisting){
26878         if(!keepExisting){
26879             this.clearSelections();
26880         }
26881         for(var i = 0, len = rows.length; i < len; i++){
26882             this.selectRow(rows[i], true);
26883         }
26884     },
26885
26886     /**
26887      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26888      * @param {Number} startRow The index of the first row in the range
26889      * @param {Number} endRow The index of the last row in the range
26890      * @param {Boolean} keepExisting (optional) True to retain existing selections
26891      */
26892     selectRange : function(startRow, endRow, keepExisting){
26893         if(this.locked) {
26894             return;
26895         }
26896         if(!keepExisting){
26897             this.clearSelections();
26898         }
26899         if(startRow <= endRow){
26900             for(var i = startRow; i <= endRow; i++){
26901                 this.selectRow(i, true);
26902             }
26903         }else{
26904             for(var i = startRow; i >= endRow; i--){
26905                 this.selectRow(i, true);
26906             }
26907         }
26908     },
26909
26910     /**
26911      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26912      * @param {Number} startRow The index of the first row in the range
26913      * @param {Number} endRow The index of the last row in the range
26914      */
26915     deselectRange : function(startRow, endRow, preventViewNotify){
26916         if(this.locked) {
26917             return;
26918         }
26919         for(var i = startRow; i <= endRow; i++){
26920             this.deselectRow(i, preventViewNotify);
26921         }
26922     },
26923
26924     /**
26925      * Selects a row.
26926      * @param {Number} row The index of the row to select
26927      * @param {Boolean} keepExisting (optional) True to keep existing selections
26928      */
26929     selectRow : function(index, keepExisting, preventViewNotify)
26930     {
26931             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26932             return;
26933         }
26934         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26935             if(!keepExisting || this.singleSelect){
26936                 this.clearSelections();
26937             }
26938             
26939             var r = this.grid.store.getAt(index);
26940             //console.log('selectRow - record id :' + r.id);
26941             
26942             this.selections.add(r);
26943             this.last = this.lastActive = index;
26944             if(!preventViewNotify){
26945                 var proxy = new Roo.Element(
26946                                 this.grid.getRowDom(index)
26947                 );
26948                 proxy.addClass('bg-info info');
26949             }
26950             this.fireEvent("rowselect", this, index, r);
26951             this.fireEvent("selectionchange", this);
26952         }
26953     },
26954
26955     /**
26956      * Deselects a row.
26957      * @param {Number} row The index of the row to deselect
26958      */
26959     deselectRow : function(index, preventViewNotify)
26960     {
26961         if(this.locked) {
26962             return;
26963         }
26964         if(this.last == index){
26965             this.last = false;
26966         }
26967         if(this.lastActive == index){
26968             this.lastActive = false;
26969         }
26970         
26971         var r = this.grid.store.getAt(index);
26972         if (!r) {
26973             return;
26974         }
26975         
26976         this.selections.remove(r);
26977         //.console.log('deselectRow - record id :' + r.id);
26978         if(!preventViewNotify){
26979         
26980             var proxy = new Roo.Element(
26981                 this.grid.getRowDom(index)
26982             );
26983             proxy.removeClass('bg-info info');
26984         }
26985         this.fireEvent("rowdeselect", this, index);
26986         this.fireEvent("selectionchange", this);
26987     },
26988
26989     // private
26990     restoreLast : function(){
26991         if(this._last){
26992             this.last = this._last;
26993         }
26994     },
26995
26996     // private
26997     acceptsNav : function(row, col, cm){
26998         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26999     },
27000
27001     // private
27002     onEditorKey : function(field, e){
27003         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27004         if(k == e.TAB){
27005             e.stopEvent();
27006             ed.completeEdit();
27007             if(e.shiftKey){
27008                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27009             }else{
27010                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27011             }
27012         }else if(k == e.ENTER && !e.ctrlKey){
27013             e.stopEvent();
27014             ed.completeEdit();
27015             if(e.shiftKey){
27016                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27017             }else{
27018                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27019             }
27020         }else if(k == e.ESC){
27021             ed.cancelEdit();
27022         }
27023         if(newCell){
27024             g.startEditing(newCell[0], newCell[1]);
27025         }
27026     }
27027 });
27028 /*
27029  * Based on:
27030  * Ext JS Library 1.1.1
27031  * Copyright(c) 2006-2007, Ext JS, LLC.
27032  *
27033  * Originally Released Under LGPL - original licence link has changed is not relivant.
27034  *
27035  * Fork - LGPL
27036  * <script type="text/javascript">
27037  */
27038  
27039 /**
27040  * @class Roo.bootstrap.PagingToolbar
27041  * @extends Roo.bootstrap.NavSimplebar
27042  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27043  * @constructor
27044  * Create a new PagingToolbar
27045  * @param {Object} config The config object
27046  * @param {Roo.data.Store} store
27047  */
27048 Roo.bootstrap.PagingToolbar = function(config)
27049 {
27050     // old args format still supported... - xtype is prefered..
27051         // created from xtype...
27052     
27053     this.ds = config.dataSource;
27054     
27055     if (config.store && !this.ds) {
27056         this.store= Roo.factory(config.store, Roo.data);
27057         this.ds = this.store;
27058         this.ds.xmodule = this.xmodule || false;
27059     }
27060     
27061     this.toolbarItems = [];
27062     if (config.items) {
27063         this.toolbarItems = config.items;
27064     }
27065     
27066     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27067     
27068     this.cursor = 0;
27069     
27070     if (this.ds) { 
27071         this.bind(this.ds);
27072     }
27073     
27074     if (Roo.bootstrap.version == 4) {
27075         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27076     } else {
27077         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27078     }
27079     
27080 };
27081
27082 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27083     /**
27084      * @cfg {Roo.data.Store} dataSource
27085      * The underlying data store providing the paged data
27086      */
27087     /**
27088      * @cfg {String/HTMLElement/Element} container
27089      * container The id or element that will contain the toolbar
27090      */
27091     /**
27092      * @cfg {Boolean} displayInfo
27093      * True to display the displayMsg (defaults to false)
27094      */
27095     /**
27096      * @cfg {Number} pageSize
27097      * The number of records to display per page (defaults to 20)
27098      */
27099     pageSize: 20,
27100     /**
27101      * @cfg {String} displayMsg
27102      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27103      */
27104     displayMsg : 'Displaying {0} - {1} of {2}',
27105     /**
27106      * @cfg {String} emptyMsg
27107      * The message to display when no records are found (defaults to "No data to display")
27108      */
27109     emptyMsg : 'No data to display',
27110     /**
27111      * Customizable piece of the default paging text (defaults to "Page")
27112      * @type String
27113      */
27114     beforePageText : "Page",
27115     /**
27116      * Customizable piece of the default paging text (defaults to "of %0")
27117      * @type String
27118      */
27119     afterPageText : "of {0}",
27120     /**
27121      * Customizable piece of the default paging text (defaults to "First Page")
27122      * @type String
27123      */
27124     firstText : "First Page",
27125     /**
27126      * Customizable piece of the default paging text (defaults to "Previous Page")
27127      * @type String
27128      */
27129     prevText : "Previous Page",
27130     /**
27131      * Customizable piece of the default paging text (defaults to "Next Page")
27132      * @type String
27133      */
27134     nextText : "Next Page",
27135     /**
27136      * Customizable piece of the default paging text (defaults to "Last Page")
27137      * @type String
27138      */
27139     lastText : "Last Page",
27140     /**
27141      * Customizable piece of the default paging text (defaults to "Refresh")
27142      * @type String
27143      */
27144     refreshText : "Refresh",
27145
27146     buttons : false,
27147     // private
27148     onRender : function(ct, position) 
27149     {
27150         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27151         this.navgroup.parentId = this.id;
27152         this.navgroup.onRender(this.el, null);
27153         // add the buttons to the navgroup
27154         
27155         if(this.displayInfo){
27156             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27157             this.displayEl = this.el.select('.x-paging-info', true).first();
27158 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27159 //            this.displayEl = navel.el.select('span',true).first();
27160         }
27161         
27162         var _this = this;
27163         
27164         if(this.buttons){
27165             Roo.each(_this.buttons, function(e){ // this might need to use render????
27166                Roo.factory(e).render(_this.el);
27167             });
27168         }
27169             
27170         Roo.each(_this.toolbarItems, function(e) {
27171             _this.navgroup.addItem(e);
27172         });
27173         
27174         
27175         this.first = this.navgroup.addItem({
27176             tooltip: this.firstText,
27177             cls: "prev btn-outline-secondary",
27178             html : ' <i class="fa fa-step-backward"></i>',
27179             disabled: true,
27180             preventDefault: true,
27181             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27182         });
27183         
27184         this.prev =  this.navgroup.addItem({
27185             tooltip: this.prevText,
27186             cls: "prev btn-outline-secondary",
27187             html : ' <i class="fa fa-backward"></i>',
27188             disabled: true,
27189             preventDefault: true,
27190             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27191         });
27192     //this.addSeparator();
27193         
27194         
27195         var field = this.navgroup.addItem( {
27196             tagtype : 'span',
27197             cls : 'x-paging-position  btn-outline-secondary',
27198              disabled: true,
27199             html : this.beforePageText  +
27200                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27201                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27202          } ); //?? escaped?
27203         
27204         this.field = field.el.select('input', true).first();
27205         this.field.on("keydown", this.onPagingKeydown, this);
27206         this.field.on("focus", function(){this.dom.select();});
27207     
27208     
27209         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27210         //this.field.setHeight(18);
27211         //this.addSeparator();
27212         this.next = this.navgroup.addItem({
27213             tooltip: this.nextText,
27214             cls: "next btn-outline-secondary",
27215             html : ' <i class="fa fa-forward"></i>',
27216             disabled: true,
27217             preventDefault: true,
27218             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27219         });
27220         this.last = this.navgroup.addItem({
27221             tooltip: this.lastText,
27222             html : ' <i class="fa fa-step-forward"></i>',
27223             cls: "next btn-outline-secondary",
27224             disabled: true,
27225             preventDefault: true,
27226             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27227         });
27228     //this.addSeparator();
27229         this.loading = this.navgroup.addItem({
27230             tooltip: this.refreshText,
27231             cls: "btn-outline-secondary",
27232             html : ' <i class="fa fa-refresh"></i>',
27233             preventDefault: true,
27234             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27235         });
27236         
27237     },
27238
27239     // private
27240     updateInfo : function(){
27241         if(this.displayEl){
27242             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27243             var msg = count == 0 ?
27244                 this.emptyMsg :
27245                 String.format(
27246                     this.displayMsg,
27247                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27248                 );
27249             this.displayEl.update(msg);
27250         }
27251     },
27252
27253     // private
27254     onLoad : function(ds, r, o)
27255     {
27256         this.cursor = o.params.start ? o.params.start : 0;
27257         
27258         var d = this.getPageData(),
27259             ap = d.activePage,
27260             ps = d.pages;
27261         
27262         
27263         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27264         this.field.dom.value = ap;
27265         this.first.setDisabled(ap == 1);
27266         this.prev.setDisabled(ap == 1);
27267         this.next.setDisabled(ap == ps);
27268         this.last.setDisabled(ap == ps);
27269         this.loading.enable();
27270         this.updateInfo();
27271     },
27272
27273     // private
27274     getPageData : function(){
27275         var total = this.ds.getTotalCount();
27276         return {
27277             total : total,
27278             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27279             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27280         };
27281     },
27282
27283     // private
27284     onLoadError : function(){
27285         this.loading.enable();
27286     },
27287
27288     // private
27289     onPagingKeydown : function(e){
27290         var k = e.getKey();
27291         var d = this.getPageData();
27292         if(k == e.RETURN){
27293             var v = this.field.dom.value, pageNum;
27294             if(!v || isNaN(pageNum = parseInt(v, 10))){
27295                 this.field.dom.value = d.activePage;
27296                 return;
27297             }
27298             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27299             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27300             e.stopEvent();
27301         }
27302         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))
27303         {
27304           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27305           this.field.dom.value = pageNum;
27306           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27307           e.stopEvent();
27308         }
27309         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27310         {
27311           var v = this.field.dom.value, pageNum; 
27312           var increment = (e.shiftKey) ? 10 : 1;
27313           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27314                 increment *= -1;
27315           }
27316           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27317             this.field.dom.value = d.activePage;
27318             return;
27319           }
27320           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27321           {
27322             this.field.dom.value = parseInt(v, 10) + increment;
27323             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27324             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27325           }
27326           e.stopEvent();
27327         }
27328     },
27329
27330     // private
27331     beforeLoad : function(){
27332         if(this.loading){
27333             this.loading.disable();
27334         }
27335     },
27336
27337     // private
27338     onClick : function(which){
27339         
27340         var ds = this.ds;
27341         if (!ds) {
27342             return;
27343         }
27344         
27345         switch(which){
27346             case "first":
27347                 ds.load({params:{start: 0, limit: this.pageSize}});
27348             break;
27349             case "prev":
27350                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27351             break;
27352             case "next":
27353                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27354             break;
27355             case "last":
27356                 var total = ds.getTotalCount();
27357                 var extra = total % this.pageSize;
27358                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27359                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27360             break;
27361             case "refresh":
27362                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27363             break;
27364         }
27365     },
27366
27367     /**
27368      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27369      * @param {Roo.data.Store} store The data store to unbind
27370      */
27371     unbind : function(ds){
27372         ds.un("beforeload", this.beforeLoad, this);
27373         ds.un("load", this.onLoad, this);
27374         ds.un("loadexception", this.onLoadError, this);
27375         ds.un("remove", this.updateInfo, this);
27376         ds.un("add", this.updateInfo, this);
27377         this.ds = undefined;
27378     },
27379
27380     /**
27381      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27382      * @param {Roo.data.Store} store The data store to bind
27383      */
27384     bind : function(ds){
27385         ds.on("beforeload", this.beforeLoad, this);
27386         ds.on("load", this.onLoad, this);
27387         ds.on("loadexception", this.onLoadError, this);
27388         ds.on("remove", this.updateInfo, this);
27389         ds.on("add", this.updateInfo, this);
27390         this.ds = ds;
27391     }
27392 });/*
27393  * - LGPL
27394  *
27395  * element
27396  * 
27397  */
27398
27399 /**
27400  * @class Roo.bootstrap.MessageBar
27401  * @extends Roo.bootstrap.Component
27402  * Bootstrap MessageBar class
27403  * @cfg {String} html contents of the MessageBar
27404  * @cfg {String} weight (info | success | warning | danger) default info
27405  * @cfg {String} beforeClass insert the bar before the given class
27406  * @cfg {Boolean} closable (true | false) default false
27407  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27408  * 
27409  * @constructor
27410  * Create a new Element
27411  * @param {Object} config The config object
27412  */
27413
27414 Roo.bootstrap.MessageBar = function(config){
27415     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27416 };
27417
27418 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27419     
27420     html: '',
27421     weight: 'info',
27422     closable: false,
27423     fixed: false,
27424     beforeClass: 'bootstrap-sticky-wrap',
27425     
27426     getAutoCreate : function(){
27427         
27428         var cfg = {
27429             tag: 'div',
27430             cls: 'alert alert-dismissable alert-' + this.weight,
27431             cn: [
27432                 {
27433                     tag: 'span',
27434                     cls: 'message',
27435                     html: this.html || ''
27436                 }
27437             ]
27438         };
27439         
27440         if(this.fixed){
27441             cfg.cls += ' alert-messages-fixed';
27442         }
27443         
27444         if(this.closable){
27445             cfg.cn.push({
27446                 tag: 'button',
27447                 cls: 'close',
27448                 html: 'x'
27449             });
27450         }
27451         
27452         return cfg;
27453     },
27454     
27455     onRender : function(ct, position)
27456     {
27457         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27458         
27459         if(!this.el){
27460             var cfg = Roo.apply({},  this.getAutoCreate());
27461             cfg.id = Roo.id();
27462             
27463             if (this.cls) {
27464                 cfg.cls += ' ' + this.cls;
27465             }
27466             if (this.style) {
27467                 cfg.style = this.style;
27468             }
27469             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27470             
27471             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27472         }
27473         
27474         this.el.select('>button.close').on('click', this.hide, this);
27475         
27476     },
27477     
27478     show : function()
27479     {
27480         if (!this.rendered) {
27481             this.render();
27482         }
27483         
27484         this.el.show();
27485         
27486         this.fireEvent('show', this);
27487         
27488     },
27489     
27490     hide : function()
27491     {
27492         if (!this.rendered) {
27493             this.render();
27494         }
27495         
27496         this.el.hide();
27497         
27498         this.fireEvent('hide', this);
27499     },
27500     
27501     update : function()
27502     {
27503 //        var e = this.el.dom.firstChild;
27504 //        
27505 //        if(this.closable){
27506 //            e = e.nextSibling;
27507 //        }
27508 //        
27509 //        e.data = this.html || '';
27510
27511         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27512     }
27513    
27514 });
27515
27516  
27517
27518      /*
27519  * - LGPL
27520  *
27521  * Graph
27522  * 
27523  */
27524
27525
27526 /**
27527  * @class Roo.bootstrap.Graph
27528  * @extends Roo.bootstrap.Component
27529  * Bootstrap Graph class
27530 > Prameters
27531  -sm {number} sm 4
27532  -md {number} md 5
27533  @cfg {String} graphtype  bar | vbar | pie
27534  @cfg {number} g_x coodinator | centre x (pie)
27535  @cfg {number} g_y coodinator | centre y (pie)
27536  @cfg {number} g_r radius (pie)
27537  @cfg {number} g_height height of the chart (respected by all elements in the set)
27538  @cfg {number} g_width width of the chart (respected by all elements in the set)
27539  @cfg {Object} title The title of the chart
27540     
27541  -{Array}  values
27542  -opts (object) options for the chart 
27543      o {
27544      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27545      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27546      o vgutter (number)
27547      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.
27548      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27549      o to
27550      o stretch (boolean)
27551      o }
27552  -opts (object) options for the pie
27553      o{
27554      o cut
27555      o startAngle (number)
27556      o endAngle (number)
27557      } 
27558  *
27559  * @constructor
27560  * Create a new Input
27561  * @param {Object} config The config object
27562  */
27563
27564 Roo.bootstrap.Graph = function(config){
27565     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27566     
27567     this.addEvents({
27568         // img events
27569         /**
27570          * @event click
27571          * The img click event for the img.
27572          * @param {Roo.EventObject} e
27573          */
27574         "click" : true
27575     });
27576 };
27577
27578 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27579     
27580     sm: 4,
27581     md: 5,
27582     graphtype: 'bar',
27583     g_height: 250,
27584     g_width: 400,
27585     g_x: 50,
27586     g_y: 50,
27587     g_r: 30,
27588     opts:{
27589         //g_colors: this.colors,
27590         g_type: 'soft',
27591         g_gutter: '20%'
27592
27593     },
27594     title : false,
27595
27596     getAutoCreate : function(){
27597         
27598         var cfg = {
27599             tag: 'div',
27600             html : null
27601         };
27602         
27603         
27604         return  cfg;
27605     },
27606
27607     onRender : function(ct,position){
27608         
27609         
27610         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27611         
27612         if (typeof(Raphael) == 'undefined') {
27613             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27614             return;
27615         }
27616         
27617         this.raphael = Raphael(this.el.dom);
27618         
27619                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27620                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27621                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27622                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27623                 /*
27624                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27625                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27626                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27627                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27628                 
27629                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27630                 r.barchart(330, 10, 300, 220, data1);
27631                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27632                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27633                 */
27634                 
27635                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27636                 // r.barchart(30, 30, 560, 250,  xdata, {
27637                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27638                 //     axis : "0 0 1 1",
27639                 //     axisxlabels :  xdata
27640                 //     //yvalues : cols,
27641                    
27642                 // });
27643 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27644 //        
27645 //        this.load(null,xdata,{
27646 //                axis : "0 0 1 1",
27647 //                axisxlabels :  xdata
27648 //                });
27649
27650     },
27651
27652     load : function(graphtype,xdata,opts)
27653     {
27654         this.raphael.clear();
27655         if(!graphtype) {
27656             graphtype = this.graphtype;
27657         }
27658         if(!opts){
27659             opts = this.opts;
27660         }
27661         var r = this.raphael,
27662             fin = function () {
27663                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27664             },
27665             fout = function () {
27666                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27667             },
27668             pfin = function() {
27669                 this.sector.stop();
27670                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27671
27672                 if (this.label) {
27673                     this.label[0].stop();
27674                     this.label[0].attr({ r: 7.5 });
27675                     this.label[1].attr({ "font-weight": 800 });
27676                 }
27677             },
27678             pfout = function() {
27679                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27680
27681                 if (this.label) {
27682                     this.label[0].animate({ r: 5 }, 500, "bounce");
27683                     this.label[1].attr({ "font-weight": 400 });
27684                 }
27685             };
27686
27687         switch(graphtype){
27688             case 'bar':
27689                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27690                 break;
27691             case 'hbar':
27692                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27693                 break;
27694             case 'pie':
27695 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27696 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27697 //            
27698                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27699                 
27700                 break;
27701
27702         }
27703         
27704         if(this.title){
27705             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27706         }
27707         
27708     },
27709     
27710     setTitle: function(o)
27711     {
27712         this.title = o;
27713     },
27714     
27715     initEvents: function() {
27716         
27717         if(!this.href){
27718             this.el.on('click', this.onClick, this);
27719         }
27720     },
27721     
27722     onClick : function(e)
27723     {
27724         Roo.log('img onclick');
27725         this.fireEvent('click', this, e);
27726     }
27727    
27728 });
27729
27730  
27731 /*
27732  * - LGPL
27733  *
27734  * numberBox
27735  * 
27736  */
27737 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27738
27739 /**
27740  * @class Roo.bootstrap.dash.NumberBox
27741  * @extends Roo.bootstrap.Component
27742  * Bootstrap NumberBox class
27743  * @cfg {String} headline Box headline
27744  * @cfg {String} content Box content
27745  * @cfg {String} icon Box icon
27746  * @cfg {String} footer Footer text
27747  * @cfg {String} fhref Footer href
27748  * 
27749  * @constructor
27750  * Create a new NumberBox
27751  * @param {Object} config The config object
27752  */
27753
27754
27755 Roo.bootstrap.dash.NumberBox = function(config){
27756     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27757     
27758 };
27759
27760 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27761     
27762     headline : '',
27763     content : '',
27764     icon : '',
27765     footer : '',
27766     fhref : '',
27767     ficon : '',
27768     
27769     getAutoCreate : function(){
27770         
27771         var cfg = {
27772             tag : 'div',
27773             cls : 'small-box ',
27774             cn : [
27775                 {
27776                     tag : 'div',
27777                     cls : 'inner',
27778                     cn :[
27779                         {
27780                             tag : 'h3',
27781                             cls : 'roo-headline',
27782                             html : this.headline
27783                         },
27784                         {
27785                             tag : 'p',
27786                             cls : 'roo-content',
27787                             html : this.content
27788                         }
27789                     ]
27790                 }
27791             ]
27792         };
27793         
27794         if(this.icon){
27795             cfg.cn.push({
27796                 tag : 'div',
27797                 cls : 'icon',
27798                 cn :[
27799                     {
27800                         tag : 'i',
27801                         cls : 'ion ' + this.icon
27802                     }
27803                 ]
27804             });
27805         }
27806         
27807         if(this.footer){
27808             var footer = {
27809                 tag : 'a',
27810                 cls : 'small-box-footer',
27811                 href : this.fhref || '#',
27812                 html : this.footer
27813             };
27814             
27815             cfg.cn.push(footer);
27816             
27817         }
27818         
27819         return  cfg;
27820     },
27821
27822     onRender : function(ct,position){
27823         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27824
27825
27826        
27827                 
27828     },
27829
27830     setHeadline: function (value)
27831     {
27832         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27833     },
27834     
27835     setFooter: function (value, href)
27836     {
27837         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27838         
27839         if(href){
27840             this.el.select('a.small-box-footer',true).first().attr('href', href);
27841         }
27842         
27843     },
27844
27845     setContent: function (value)
27846     {
27847         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27848     },
27849
27850     initEvents: function() 
27851     {   
27852         
27853     }
27854     
27855 });
27856
27857  
27858 /*
27859  * - LGPL
27860  *
27861  * TabBox
27862  * 
27863  */
27864 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27865
27866 /**
27867  * @class Roo.bootstrap.dash.TabBox
27868  * @extends Roo.bootstrap.Component
27869  * Bootstrap TabBox class
27870  * @cfg {String} title Title of the TabBox
27871  * @cfg {String} icon Icon of the TabBox
27872  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27873  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27874  * 
27875  * @constructor
27876  * Create a new TabBox
27877  * @param {Object} config The config object
27878  */
27879
27880
27881 Roo.bootstrap.dash.TabBox = function(config){
27882     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27883     this.addEvents({
27884         // raw events
27885         /**
27886          * @event addpane
27887          * When a pane is added
27888          * @param {Roo.bootstrap.dash.TabPane} pane
27889          */
27890         "addpane" : true,
27891         /**
27892          * @event activatepane
27893          * When a pane is activated
27894          * @param {Roo.bootstrap.dash.TabPane} pane
27895          */
27896         "activatepane" : true
27897         
27898          
27899     });
27900     
27901     this.panes = [];
27902 };
27903
27904 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27905
27906     title : '',
27907     icon : false,
27908     showtabs : true,
27909     tabScrollable : false,
27910     
27911     getChildContainer : function()
27912     {
27913         return this.el.select('.tab-content', true).first();
27914     },
27915     
27916     getAutoCreate : function(){
27917         
27918         var header = {
27919             tag: 'li',
27920             cls: 'pull-left header',
27921             html: this.title,
27922             cn : []
27923         };
27924         
27925         if(this.icon){
27926             header.cn.push({
27927                 tag: 'i',
27928                 cls: 'fa ' + this.icon
27929             });
27930         }
27931         
27932         var h = {
27933             tag: 'ul',
27934             cls: 'nav nav-tabs pull-right',
27935             cn: [
27936                 header
27937             ]
27938         };
27939         
27940         if(this.tabScrollable){
27941             h = {
27942                 tag: 'div',
27943                 cls: 'tab-header',
27944                 cn: [
27945                     {
27946                         tag: 'ul',
27947                         cls: 'nav nav-tabs pull-right',
27948                         cn: [
27949                             header
27950                         ]
27951                     }
27952                 ]
27953             };
27954         }
27955         
27956         var cfg = {
27957             tag: 'div',
27958             cls: 'nav-tabs-custom',
27959             cn: [
27960                 h,
27961                 {
27962                     tag: 'div',
27963                     cls: 'tab-content no-padding',
27964                     cn: []
27965                 }
27966             ]
27967         };
27968
27969         return  cfg;
27970     },
27971     initEvents : function()
27972     {
27973         //Roo.log('add add pane handler');
27974         this.on('addpane', this.onAddPane, this);
27975     },
27976      /**
27977      * Updates the box title
27978      * @param {String} html to set the title to.
27979      */
27980     setTitle : function(value)
27981     {
27982         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27983     },
27984     onAddPane : function(pane)
27985     {
27986         this.panes.push(pane);
27987         //Roo.log('addpane');
27988         //Roo.log(pane);
27989         // tabs are rendere left to right..
27990         if(!this.showtabs){
27991             return;
27992         }
27993         
27994         var ctr = this.el.select('.nav-tabs', true).first();
27995          
27996          
27997         var existing = ctr.select('.nav-tab',true);
27998         var qty = existing.getCount();;
27999         
28000         
28001         var tab = ctr.createChild({
28002             tag : 'li',
28003             cls : 'nav-tab' + (qty ? '' : ' active'),
28004             cn : [
28005                 {
28006                     tag : 'a',
28007                     href:'#',
28008                     html : pane.title
28009                 }
28010             ]
28011         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28012         pane.tab = tab;
28013         
28014         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28015         if (!qty) {
28016             pane.el.addClass('active');
28017         }
28018         
28019                 
28020     },
28021     onTabClick : function(ev,un,ob,pane)
28022     {
28023         //Roo.log('tab - prev default');
28024         ev.preventDefault();
28025         
28026         
28027         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28028         pane.tab.addClass('active');
28029         //Roo.log(pane.title);
28030         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28031         // technically we should have a deactivate event.. but maybe add later.
28032         // and it should not de-activate the selected tab...
28033         this.fireEvent('activatepane', pane);
28034         pane.el.addClass('active');
28035         pane.fireEvent('activate');
28036         
28037         
28038     },
28039     
28040     getActivePane : function()
28041     {
28042         var r = false;
28043         Roo.each(this.panes, function(p) {
28044             if(p.el.hasClass('active')){
28045                 r = p;
28046                 return false;
28047             }
28048             
28049             return;
28050         });
28051         
28052         return r;
28053     }
28054     
28055     
28056 });
28057
28058  
28059 /*
28060  * - LGPL
28061  *
28062  * Tab pane
28063  * 
28064  */
28065 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28066 /**
28067  * @class Roo.bootstrap.TabPane
28068  * @extends Roo.bootstrap.Component
28069  * Bootstrap TabPane class
28070  * @cfg {Boolean} active (false | true) Default false
28071  * @cfg {String} title title of panel
28072
28073  * 
28074  * @constructor
28075  * Create a new TabPane
28076  * @param {Object} config The config object
28077  */
28078
28079 Roo.bootstrap.dash.TabPane = function(config){
28080     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28081     
28082     this.addEvents({
28083         // raw events
28084         /**
28085          * @event activate
28086          * When a pane is activated
28087          * @param {Roo.bootstrap.dash.TabPane} pane
28088          */
28089         "activate" : true
28090          
28091     });
28092 };
28093
28094 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28095     
28096     active : false,
28097     title : '',
28098     
28099     // the tabBox that this is attached to.
28100     tab : false,
28101      
28102     getAutoCreate : function() 
28103     {
28104         var cfg = {
28105             tag: 'div',
28106             cls: 'tab-pane'
28107         };
28108         
28109         if(this.active){
28110             cfg.cls += ' active';
28111         }
28112         
28113         return cfg;
28114     },
28115     initEvents  : function()
28116     {
28117         //Roo.log('trigger add pane handler');
28118         this.parent().fireEvent('addpane', this)
28119     },
28120     
28121      /**
28122      * Updates the tab title 
28123      * @param {String} html to set the title to.
28124      */
28125     setTitle: function(str)
28126     {
28127         if (!this.tab) {
28128             return;
28129         }
28130         this.title = str;
28131         this.tab.select('a', true).first().dom.innerHTML = str;
28132         
28133     }
28134     
28135     
28136     
28137 });
28138
28139  
28140
28141
28142  /*
28143  * - LGPL
28144  *
28145  * menu
28146  * 
28147  */
28148 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28149
28150 /**
28151  * @class Roo.bootstrap.menu.Menu
28152  * @extends Roo.bootstrap.Component
28153  * Bootstrap Menu class - container for Menu
28154  * @cfg {String} html Text of the menu
28155  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28156  * @cfg {String} icon Font awesome icon
28157  * @cfg {String} pos Menu align to (top | bottom) default bottom
28158  * 
28159  * 
28160  * @constructor
28161  * Create a new Menu
28162  * @param {Object} config The config object
28163  */
28164
28165
28166 Roo.bootstrap.menu.Menu = function(config){
28167     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28168     
28169     this.addEvents({
28170         /**
28171          * @event beforeshow
28172          * Fires before this menu is displayed
28173          * @param {Roo.bootstrap.menu.Menu} this
28174          */
28175         beforeshow : true,
28176         /**
28177          * @event beforehide
28178          * Fires before this menu is hidden
28179          * @param {Roo.bootstrap.menu.Menu} this
28180          */
28181         beforehide : true,
28182         /**
28183          * @event show
28184          * Fires after this menu is displayed
28185          * @param {Roo.bootstrap.menu.Menu} this
28186          */
28187         show : true,
28188         /**
28189          * @event hide
28190          * Fires after this menu is hidden
28191          * @param {Roo.bootstrap.menu.Menu} this
28192          */
28193         hide : true,
28194         /**
28195          * @event click
28196          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28197          * @param {Roo.bootstrap.menu.Menu} this
28198          * @param {Roo.EventObject} e
28199          */
28200         click : true
28201     });
28202     
28203 };
28204
28205 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28206     
28207     submenu : false,
28208     html : '',
28209     weight : 'default',
28210     icon : false,
28211     pos : 'bottom',
28212     
28213     
28214     getChildContainer : function() {
28215         if(this.isSubMenu){
28216             return this.el;
28217         }
28218         
28219         return this.el.select('ul.dropdown-menu', true).first();  
28220     },
28221     
28222     getAutoCreate : function()
28223     {
28224         var text = [
28225             {
28226                 tag : 'span',
28227                 cls : 'roo-menu-text',
28228                 html : this.html
28229             }
28230         ];
28231         
28232         if(this.icon){
28233             text.unshift({
28234                 tag : 'i',
28235                 cls : 'fa ' + this.icon
28236             })
28237         }
28238         
28239         
28240         var cfg = {
28241             tag : 'div',
28242             cls : 'btn-group',
28243             cn : [
28244                 {
28245                     tag : 'button',
28246                     cls : 'dropdown-button btn btn-' + this.weight,
28247                     cn : text
28248                 },
28249                 {
28250                     tag : 'button',
28251                     cls : 'dropdown-toggle btn btn-' + this.weight,
28252                     cn : [
28253                         {
28254                             tag : 'span',
28255                             cls : 'caret'
28256                         }
28257                     ]
28258                 },
28259                 {
28260                     tag : 'ul',
28261                     cls : 'dropdown-menu'
28262                 }
28263             ]
28264             
28265         };
28266         
28267         if(this.pos == 'top'){
28268             cfg.cls += ' dropup';
28269         }
28270         
28271         if(this.isSubMenu){
28272             cfg = {
28273                 tag : 'ul',
28274                 cls : 'dropdown-menu'
28275             }
28276         }
28277         
28278         return cfg;
28279     },
28280     
28281     onRender : function(ct, position)
28282     {
28283         this.isSubMenu = ct.hasClass('dropdown-submenu');
28284         
28285         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28286     },
28287     
28288     initEvents : function() 
28289     {
28290         if(this.isSubMenu){
28291             return;
28292         }
28293         
28294         this.hidden = true;
28295         
28296         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28297         this.triggerEl.on('click', this.onTriggerPress, this);
28298         
28299         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28300         this.buttonEl.on('click', this.onClick, this);
28301         
28302     },
28303     
28304     list : function()
28305     {
28306         if(this.isSubMenu){
28307             return this.el;
28308         }
28309         
28310         return this.el.select('ul.dropdown-menu', true).first();
28311     },
28312     
28313     onClick : function(e)
28314     {
28315         this.fireEvent("click", this, e);
28316     },
28317     
28318     onTriggerPress  : function(e)
28319     {   
28320         if (this.isVisible()) {
28321             this.hide();
28322         } else {
28323             this.show();
28324         }
28325     },
28326     
28327     isVisible : function(){
28328         return !this.hidden;
28329     },
28330     
28331     show : function()
28332     {
28333         this.fireEvent("beforeshow", this);
28334         
28335         this.hidden = false;
28336         this.el.addClass('open');
28337         
28338         Roo.get(document).on("mouseup", this.onMouseUp, this);
28339         
28340         this.fireEvent("show", this);
28341         
28342         
28343     },
28344     
28345     hide : function()
28346     {
28347         this.fireEvent("beforehide", this);
28348         
28349         this.hidden = true;
28350         this.el.removeClass('open');
28351         
28352         Roo.get(document).un("mouseup", this.onMouseUp);
28353         
28354         this.fireEvent("hide", this);
28355     },
28356     
28357     onMouseUp : function()
28358     {
28359         this.hide();
28360     }
28361     
28362 });
28363
28364  
28365  /*
28366  * - LGPL
28367  *
28368  * menu item
28369  * 
28370  */
28371 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28372
28373 /**
28374  * @class Roo.bootstrap.menu.Item
28375  * @extends Roo.bootstrap.Component
28376  * Bootstrap MenuItem class
28377  * @cfg {Boolean} submenu (true | false) default false
28378  * @cfg {String} html text of the item
28379  * @cfg {String} href the link
28380  * @cfg {Boolean} disable (true | false) default false
28381  * @cfg {Boolean} preventDefault (true | false) default true
28382  * @cfg {String} icon Font awesome icon
28383  * @cfg {String} pos Submenu align to (left | right) default right 
28384  * 
28385  * 
28386  * @constructor
28387  * Create a new Item
28388  * @param {Object} config The config object
28389  */
28390
28391
28392 Roo.bootstrap.menu.Item = function(config){
28393     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28394     this.addEvents({
28395         /**
28396          * @event mouseover
28397          * Fires when the mouse is hovering over this menu
28398          * @param {Roo.bootstrap.menu.Item} this
28399          * @param {Roo.EventObject} e
28400          */
28401         mouseover : true,
28402         /**
28403          * @event mouseout
28404          * Fires when the mouse exits this menu
28405          * @param {Roo.bootstrap.menu.Item} this
28406          * @param {Roo.EventObject} e
28407          */
28408         mouseout : true,
28409         // raw events
28410         /**
28411          * @event click
28412          * The raw click event for the entire grid.
28413          * @param {Roo.EventObject} e
28414          */
28415         click : true
28416     });
28417 };
28418
28419 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28420     
28421     submenu : false,
28422     href : '',
28423     html : '',
28424     preventDefault: true,
28425     disable : false,
28426     icon : false,
28427     pos : 'right',
28428     
28429     getAutoCreate : function()
28430     {
28431         var text = [
28432             {
28433                 tag : 'span',
28434                 cls : 'roo-menu-item-text',
28435                 html : this.html
28436             }
28437         ];
28438         
28439         if(this.icon){
28440             text.unshift({
28441                 tag : 'i',
28442                 cls : 'fa ' + this.icon
28443             })
28444         }
28445         
28446         var cfg = {
28447             tag : 'li',
28448             cn : [
28449                 {
28450                     tag : 'a',
28451                     href : this.href || '#',
28452                     cn : text
28453                 }
28454             ]
28455         };
28456         
28457         if(this.disable){
28458             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28459         }
28460         
28461         if(this.submenu){
28462             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28463             
28464             if(this.pos == 'left'){
28465                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28466             }
28467         }
28468         
28469         return cfg;
28470     },
28471     
28472     initEvents : function() 
28473     {
28474         this.el.on('mouseover', this.onMouseOver, this);
28475         this.el.on('mouseout', this.onMouseOut, this);
28476         
28477         this.el.select('a', true).first().on('click', this.onClick, this);
28478         
28479     },
28480     
28481     onClick : function(e)
28482     {
28483         if(this.preventDefault){
28484             e.preventDefault();
28485         }
28486         
28487         this.fireEvent("click", this, e);
28488     },
28489     
28490     onMouseOver : function(e)
28491     {
28492         if(this.submenu && this.pos == 'left'){
28493             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28494         }
28495         
28496         this.fireEvent("mouseover", this, e);
28497     },
28498     
28499     onMouseOut : function(e)
28500     {
28501         this.fireEvent("mouseout", this, e);
28502     }
28503 });
28504
28505  
28506
28507  /*
28508  * - LGPL
28509  *
28510  * menu separator
28511  * 
28512  */
28513 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28514
28515 /**
28516  * @class Roo.bootstrap.menu.Separator
28517  * @extends Roo.bootstrap.Component
28518  * Bootstrap Separator class
28519  * 
28520  * @constructor
28521  * Create a new Separator
28522  * @param {Object} config The config object
28523  */
28524
28525
28526 Roo.bootstrap.menu.Separator = function(config){
28527     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28528 };
28529
28530 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28531     
28532     getAutoCreate : function(){
28533         var cfg = {
28534             tag : 'li',
28535             cls: 'divider'
28536         };
28537         
28538         return cfg;
28539     }
28540    
28541 });
28542
28543  
28544
28545  /*
28546  * - LGPL
28547  *
28548  * Tooltip
28549  * 
28550  */
28551
28552 /**
28553  * @class Roo.bootstrap.Tooltip
28554  * Bootstrap Tooltip class
28555  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28556  * to determine which dom element triggers the tooltip.
28557  * 
28558  * It needs to add support for additional attributes like tooltip-position
28559  * 
28560  * @constructor
28561  * Create a new Toolti
28562  * @param {Object} config The config object
28563  */
28564
28565 Roo.bootstrap.Tooltip = function(config){
28566     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28567     
28568     this.alignment = Roo.bootstrap.Tooltip.alignment;
28569     
28570     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28571         this.alignment = config.alignment;
28572     }
28573     
28574 };
28575
28576 Roo.apply(Roo.bootstrap.Tooltip, {
28577     /**
28578      * @function init initialize tooltip monitoring.
28579      * @static
28580      */
28581     currentEl : false,
28582     currentTip : false,
28583     currentRegion : false,
28584     
28585     //  init : delay?
28586     
28587     init : function()
28588     {
28589         Roo.get(document).on('mouseover', this.enter ,this);
28590         Roo.get(document).on('mouseout', this.leave, this);
28591          
28592         
28593         this.currentTip = new Roo.bootstrap.Tooltip();
28594     },
28595     
28596     enter : function(ev)
28597     {
28598         var dom = ev.getTarget();
28599         
28600         //Roo.log(['enter',dom]);
28601         var el = Roo.fly(dom);
28602         if (this.currentEl) {
28603             //Roo.log(dom);
28604             //Roo.log(this.currentEl);
28605             //Roo.log(this.currentEl.contains(dom));
28606             if (this.currentEl == el) {
28607                 return;
28608             }
28609             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28610                 return;
28611             }
28612
28613         }
28614         
28615         if (this.currentTip.el) {
28616             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28617         }    
28618         //Roo.log(ev);
28619         
28620         if(!el || el.dom == document){
28621             return;
28622         }
28623         
28624         var bindEl = el;
28625         
28626         // you can not look for children, as if el is the body.. then everythign is the child..
28627         if (!el.attr('tooltip')) { //
28628             if (!el.select("[tooltip]").elements.length) {
28629                 return;
28630             }
28631             // is the mouse over this child...?
28632             bindEl = el.select("[tooltip]").first();
28633             var xy = ev.getXY();
28634             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28635                 //Roo.log("not in region.");
28636                 return;
28637             }
28638             //Roo.log("child element over..");
28639             
28640         }
28641         this.currentEl = bindEl;
28642         this.currentTip.bind(bindEl);
28643         this.currentRegion = Roo.lib.Region.getRegion(dom);
28644         this.currentTip.enter();
28645         
28646     },
28647     leave : function(ev)
28648     {
28649         var dom = ev.getTarget();
28650         //Roo.log(['leave',dom]);
28651         if (!this.currentEl) {
28652             return;
28653         }
28654         
28655         
28656         if (dom != this.currentEl.dom) {
28657             return;
28658         }
28659         var xy = ev.getXY();
28660         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28661             return;
28662         }
28663         // only activate leave if mouse cursor is outside... bounding box..
28664         
28665         
28666         
28667         
28668         if (this.currentTip) {
28669             this.currentTip.leave();
28670         }
28671         //Roo.log('clear currentEl');
28672         this.currentEl = false;
28673         
28674         
28675     },
28676     alignment : {
28677         'left' : ['r-l', [-2,0], 'right'],
28678         'right' : ['l-r', [2,0], 'left'],
28679         'bottom' : ['t-b', [0,2], 'top'],
28680         'top' : [ 'b-t', [0,-2], 'bottom']
28681     }
28682     
28683 });
28684
28685
28686 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28687     
28688     
28689     bindEl : false,
28690     
28691     delay : null, // can be { show : 300 , hide: 500}
28692     
28693     timeout : null,
28694     
28695     hoverState : null, //???
28696     
28697     placement : 'bottom', 
28698     
28699     alignment : false,
28700     
28701     getAutoCreate : function(){
28702     
28703         var cfg = {
28704            cls : 'tooltip',   
28705            role : 'tooltip',
28706            cn : [
28707                 {
28708                     cls : 'tooltip-arrow arrow'
28709                 },
28710                 {
28711                     cls : 'tooltip-inner'
28712                 }
28713            ]
28714         };
28715         
28716         return cfg;
28717     },
28718     bind : function(el)
28719     {
28720         this.bindEl = el;
28721     },
28722     
28723     initEvents : function()
28724     {
28725         this.arrowEl = this.el.select('.arrow', true).first();
28726         this.innerEl = this.el.select('.tooltip-inner', true).first();
28727     },
28728     
28729     enter : function () {
28730        
28731         if (this.timeout != null) {
28732             clearTimeout(this.timeout);
28733         }
28734         
28735         this.hoverState = 'in';
28736          //Roo.log("enter - show");
28737         if (!this.delay || !this.delay.show) {
28738             this.show();
28739             return;
28740         }
28741         var _t = this;
28742         this.timeout = setTimeout(function () {
28743             if (_t.hoverState == 'in') {
28744                 _t.show();
28745             }
28746         }, this.delay.show);
28747     },
28748     leave : function()
28749     {
28750         clearTimeout(this.timeout);
28751     
28752         this.hoverState = 'out';
28753          if (!this.delay || !this.delay.hide) {
28754             this.hide();
28755             return;
28756         }
28757        
28758         var _t = this;
28759         this.timeout = setTimeout(function () {
28760             //Roo.log("leave - timeout");
28761             
28762             if (_t.hoverState == 'out') {
28763                 _t.hide();
28764                 Roo.bootstrap.Tooltip.currentEl = false;
28765             }
28766         }, delay);
28767     },
28768     
28769     show : function (msg)
28770     {
28771         if (!this.el) {
28772             this.render(document.body);
28773         }
28774         // set content.
28775         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28776         
28777         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28778         
28779         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28780         
28781         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28782                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28783         
28784         var placement = typeof this.placement == 'function' ?
28785             this.placement.call(this, this.el, on_el) :
28786             this.placement;
28787             
28788         var autoToken = /\s?auto?\s?/i;
28789         var autoPlace = autoToken.test(placement);
28790         if (autoPlace) {
28791             placement = placement.replace(autoToken, '') || 'top';
28792         }
28793         
28794         //this.el.detach()
28795         //this.el.setXY([0,0]);
28796         this.el.show();
28797         //this.el.dom.style.display='block';
28798         
28799         //this.el.appendTo(on_el);
28800         
28801         var p = this.getPosition();
28802         var box = this.el.getBox();
28803         
28804         if (autoPlace) {
28805             // fixme..
28806         }
28807         
28808         var align = this.alignment[placement];
28809         
28810         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28811         
28812         if(placement == 'top' || placement == 'bottom'){
28813             if(xy[0] < 0){
28814                 placement = 'right';
28815             }
28816             
28817             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28818                 placement = 'left';
28819             }
28820             
28821             var scroll = Roo.select('body', true).first().getScroll();
28822             
28823             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28824                 placement = 'top';
28825             }
28826             
28827             align = this.alignment[placement];
28828             
28829             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28830             
28831         }
28832         
28833         this.el.alignTo(this.bindEl, align[0],align[1]);
28834         //var arrow = this.el.select('.arrow',true).first();
28835         //arrow.set(align[2], 
28836         
28837         this.el.addClass(placement);
28838         this.el.addClass("bs-tooltip-"+ placement);
28839         
28840         this.el.addClass('in fade show');
28841         
28842         this.hoverState = null;
28843         
28844         if (this.el.hasClass('fade')) {
28845             // fade it?
28846         }
28847         
28848         
28849         
28850         
28851         
28852     },
28853     hide : function()
28854     {
28855          
28856         if (!this.el) {
28857             return;
28858         }
28859         //this.el.setXY([0,0]);
28860         this.el.removeClass(['show', 'in']);
28861         //this.el.hide();
28862         
28863     }
28864     
28865 });
28866  
28867
28868  /*
28869  * - LGPL
28870  *
28871  * Location Picker
28872  * 
28873  */
28874
28875 /**
28876  * @class Roo.bootstrap.LocationPicker
28877  * @extends Roo.bootstrap.Component
28878  * Bootstrap LocationPicker class
28879  * @cfg {Number} latitude Position when init default 0
28880  * @cfg {Number} longitude Position when init default 0
28881  * @cfg {Number} zoom default 15
28882  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28883  * @cfg {Boolean} mapTypeControl default false
28884  * @cfg {Boolean} disableDoubleClickZoom default false
28885  * @cfg {Boolean} scrollwheel default true
28886  * @cfg {Boolean} streetViewControl default false
28887  * @cfg {Number} radius default 0
28888  * @cfg {String} locationName
28889  * @cfg {Boolean} draggable default true
28890  * @cfg {Boolean} enableAutocomplete default false
28891  * @cfg {Boolean} enableReverseGeocode default true
28892  * @cfg {String} markerTitle
28893  * 
28894  * @constructor
28895  * Create a new LocationPicker
28896  * @param {Object} config The config object
28897  */
28898
28899
28900 Roo.bootstrap.LocationPicker = function(config){
28901     
28902     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28903     
28904     this.addEvents({
28905         /**
28906          * @event initial
28907          * Fires when the picker initialized.
28908          * @param {Roo.bootstrap.LocationPicker} this
28909          * @param {Google Location} location
28910          */
28911         initial : true,
28912         /**
28913          * @event positionchanged
28914          * Fires when the picker position changed.
28915          * @param {Roo.bootstrap.LocationPicker} this
28916          * @param {Google Location} location
28917          */
28918         positionchanged : true,
28919         /**
28920          * @event resize
28921          * Fires when the map resize.
28922          * @param {Roo.bootstrap.LocationPicker} this
28923          */
28924         resize : true,
28925         /**
28926          * @event show
28927          * Fires when the map show.
28928          * @param {Roo.bootstrap.LocationPicker} this
28929          */
28930         show : true,
28931         /**
28932          * @event hide
28933          * Fires when the map hide.
28934          * @param {Roo.bootstrap.LocationPicker} this
28935          */
28936         hide : true,
28937         /**
28938          * @event mapClick
28939          * Fires when click the map.
28940          * @param {Roo.bootstrap.LocationPicker} this
28941          * @param {Map event} e
28942          */
28943         mapClick : true,
28944         /**
28945          * @event mapRightClick
28946          * Fires when right click the map.
28947          * @param {Roo.bootstrap.LocationPicker} this
28948          * @param {Map event} e
28949          */
28950         mapRightClick : true,
28951         /**
28952          * @event markerClick
28953          * Fires when click the marker.
28954          * @param {Roo.bootstrap.LocationPicker} this
28955          * @param {Map event} e
28956          */
28957         markerClick : true,
28958         /**
28959          * @event markerRightClick
28960          * Fires when right click the marker.
28961          * @param {Roo.bootstrap.LocationPicker} this
28962          * @param {Map event} e
28963          */
28964         markerRightClick : true,
28965         /**
28966          * @event OverlayViewDraw
28967          * Fires when OverlayView Draw
28968          * @param {Roo.bootstrap.LocationPicker} this
28969          */
28970         OverlayViewDraw : true,
28971         /**
28972          * @event OverlayViewOnAdd
28973          * Fires when OverlayView Draw
28974          * @param {Roo.bootstrap.LocationPicker} this
28975          */
28976         OverlayViewOnAdd : true,
28977         /**
28978          * @event OverlayViewOnRemove
28979          * Fires when OverlayView Draw
28980          * @param {Roo.bootstrap.LocationPicker} this
28981          */
28982         OverlayViewOnRemove : true,
28983         /**
28984          * @event OverlayViewShow
28985          * Fires when OverlayView Draw
28986          * @param {Roo.bootstrap.LocationPicker} this
28987          * @param {Pixel} cpx
28988          */
28989         OverlayViewShow : true,
28990         /**
28991          * @event OverlayViewHide
28992          * Fires when OverlayView Draw
28993          * @param {Roo.bootstrap.LocationPicker} this
28994          */
28995         OverlayViewHide : true,
28996         /**
28997          * @event loadexception
28998          * Fires when load google lib failed.
28999          * @param {Roo.bootstrap.LocationPicker} this
29000          */
29001         loadexception : true
29002     });
29003         
29004 };
29005
29006 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29007     
29008     gMapContext: false,
29009     
29010     latitude: 0,
29011     longitude: 0,
29012     zoom: 15,
29013     mapTypeId: false,
29014     mapTypeControl: false,
29015     disableDoubleClickZoom: false,
29016     scrollwheel: true,
29017     streetViewControl: false,
29018     radius: 0,
29019     locationName: '',
29020     draggable: true,
29021     enableAutocomplete: false,
29022     enableReverseGeocode: true,
29023     markerTitle: '',
29024     
29025     getAutoCreate: function()
29026     {
29027
29028         var cfg = {
29029             tag: 'div',
29030             cls: 'roo-location-picker'
29031         };
29032         
29033         return cfg
29034     },
29035     
29036     initEvents: function(ct, position)
29037     {       
29038         if(!this.el.getWidth() || this.isApplied()){
29039             return;
29040         }
29041         
29042         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29043         
29044         this.initial();
29045     },
29046     
29047     initial: function()
29048     {
29049         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29050             this.fireEvent('loadexception', this);
29051             return;
29052         }
29053         
29054         if(!this.mapTypeId){
29055             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29056         }
29057         
29058         this.gMapContext = this.GMapContext();
29059         
29060         this.initOverlayView();
29061         
29062         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29063         
29064         var _this = this;
29065                 
29066         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29067             _this.setPosition(_this.gMapContext.marker.position);
29068         });
29069         
29070         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29071             _this.fireEvent('mapClick', this, event);
29072             
29073         });
29074
29075         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29076             _this.fireEvent('mapRightClick', this, event);
29077             
29078         });
29079         
29080         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29081             _this.fireEvent('markerClick', this, event);
29082             
29083         });
29084
29085         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29086             _this.fireEvent('markerRightClick', this, event);
29087             
29088         });
29089         
29090         this.setPosition(this.gMapContext.location);
29091         
29092         this.fireEvent('initial', this, this.gMapContext.location);
29093     },
29094     
29095     initOverlayView: function()
29096     {
29097         var _this = this;
29098         
29099         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29100             
29101             draw: function()
29102             {
29103                 _this.fireEvent('OverlayViewDraw', _this);
29104             },
29105             
29106             onAdd: function()
29107             {
29108                 _this.fireEvent('OverlayViewOnAdd', _this);
29109             },
29110             
29111             onRemove: function()
29112             {
29113                 _this.fireEvent('OverlayViewOnRemove', _this);
29114             },
29115             
29116             show: function(cpx)
29117             {
29118                 _this.fireEvent('OverlayViewShow', _this, cpx);
29119             },
29120             
29121             hide: function()
29122             {
29123                 _this.fireEvent('OverlayViewHide', _this);
29124             }
29125             
29126         });
29127     },
29128     
29129     fromLatLngToContainerPixel: function(event)
29130     {
29131         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29132     },
29133     
29134     isApplied: function() 
29135     {
29136         return this.getGmapContext() == false ? false : true;
29137     },
29138     
29139     getGmapContext: function() 
29140     {
29141         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29142     },
29143     
29144     GMapContext: function() 
29145     {
29146         var position = new google.maps.LatLng(this.latitude, this.longitude);
29147         
29148         var _map = new google.maps.Map(this.el.dom, {
29149             center: position,
29150             zoom: this.zoom,
29151             mapTypeId: this.mapTypeId,
29152             mapTypeControl: this.mapTypeControl,
29153             disableDoubleClickZoom: this.disableDoubleClickZoom,
29154             scrollwheel: this.scrollwheel,
29155             streetViewControl: this.streetViewControl,
29156             locationName: this.locationName,
29157             draggable: this.draggable,
29158             enableAutocomplete: this.enableAutocomplete,
29159             enableReverseGeocode: this.enableReverseGeocode
29160         });
29161         
29162         var _marker = new google.maps.Marker({
29163             position: position,
29164             map: _map,
29165             title: this.markerTitle,
29166             draggable: this.draggable
29167         });
29168         
29169         return {
29170             map: _map,
29171             marker: _marker,
29172             circle: null,
29173             location: position,
29174             radius: this.radius,
29175             locationName: this.locationName,
29176             addressComponents: {
29177                 formatted_address: null,
29178                 addressLine1: null,
29179                 addressLine2: null,
29180                 streetName: null,
29181                 streetNumber: null,
29182                 city: null,
29183                 district: null,
29184                 state: null,
29185                 stateOrProvince: null
29186             },
29187             settings: this,
29188             domContainer: this.el.dom,
29189             geodecoder: new google.maps.Geocoder()
29190         };
29191     },
29192     
29193     drawCircle: function(center, radius, options) 
29194     {
29195         if (this.gMapContext.circle != null) {
29196             this.gMapContext.circle.setMap(null);
29197         }
29198         if (radius > 0) {
29199             radius *= 1;
29200             options = Roo.apply({}, options, {
29201                 strokeColor: "#0000FF",
29202                 strokeOpacity: .35,
29203                 strokeWeight: 2,
29204                 fillColor: "#0000FF",
29205                 fillOpacity: .2
29206             });
29207             
29208             options.map = this.gMapContext.map;
29209             options.radius = radius;
29210             options.center = center;
29211             this.gMapContext.circle = new google.maps.Circle(options);
29212             return this.gMapContext.circle;
29213         }
29214         
29215         return null;
29216     },
29217     
29218     setPosition: function(location) 
29219     {
29220         this.gMapContext.location = location;
29221         this.gMapContext.marker.setPosition(location);
29222         this.gMapContext.map.panTo(location);
29223         this.drawCircle(location, this.gMapContext.radius, {});
29224         
29225         var _this = this;
29226         
29227         if (this.gMapContext.settings.enableReverseGeocode) {
29228             this.gMapContext.geodecoder.geocode({
29229                 latLng: this.gMapContext.location
29230             }, function(results, status) {
29231                 
29232                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29233                     _this.gMapContext.locationName = results[0].formatted_address;
29234                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29235                     
29236                     _this.fireEvent('positionchanged', this, location);
29237                 }
29238             });
29239             
29240             return;
29241         }
29242         
29243         this.fireEvent('positionchanged', this, location);
29244     },
29245     
29246     resize: function()
29247     {
29248         google.maps.event.trigger(this.gMapContext.map, "resize");
29249         
29250         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29251         
29252         this.fireEvent('resize', this);
29253     },
29254     
29255     setPositionByLatLng: function(latitude, longitude)
29256     {
29257         this.setPosition(new google.maps.LatLng(latitude, longitude));
29258     },
29259     
29260     getCurrentPosition: function() 
29261     {
29262         return {
29263             latitude: this.gMapContext.location.lat(),
29264             longitude: this.gMapContext.location.lng()
29265         };
29266     },
29267     
29268     getAddressName: function() 
29269     {
29270         return this.gMapContext.locationName;
29271     },
29272     
29273     getAddressComponents: function() 
29274     {
29275         return this.gMapContext.addressComponents;
29276     },
29277     
29278     address_component_from_google_geocode: function(address_components) 
29279     {
29280         var result = {};
29281         
29282         for (var i = 0; i < address_components.length; i++) {
29283             var component = address_components[i];
29284             if (component.types.indexOf("postal_code") >= 0) {
29285                 result.postalCode = component.short_name;
29286             } else if (component.types.indexOf("street_number") >= 0) {
29287                 result.streetNumber = component.short_name;
29288             } else if (component.types.indexOf("route") >= 0) {
29289                 result.streetName = component.short_name;
29290             } else if (component.types.indexOf("neighborhood") >= 0) {
29291                 result.city = component.short_name;
29292             } else if (component.types.indexOf("locality") >= 0) {
29293                 result.city = component.short_name;
29294             } else if (component.types.indexOf("sublocality") >= 0) {
29295                 result.district = component.short_name;
29296             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29297                 result.stateOrProvince = component.short_name;
29298             } else if (component.types.indexOf("country") >= 0) {
29299                 result.country = component.short_name;
29300             }
29301         }
29302         
29303         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29304         result.addressLine2 = "";
29305         return result;
29306     },
29307     
29308     setZoomLevel: function(zoom)
29309     {
29310         this.gMapContext.map.setZoom(zoom);
29311     },
29312     
29313     show: function()
29314     {
29315         if(!this.el){
29316             return;
29317         }
29318         
29319         this.el.show();
29320         
29321         this.resize();
29322         
29323         this.fireEvent('show', this);
29324     },
29325     
29326     hide: function()
29327     {
29328         if(!this.el){
29329             return;
29330         }
29331         
29332         this.el.hide();
29333         
29334         this.fireEvent('hide', this);
29335     }
29336     
29337 });
29338
29339 Roo.apply(Roo.bootstrap.LocationPicker, {
29340     
29341     OverlayView : function(map, options)
29342     {
29343         options = options || {};
29344         
29345         this.setMap(map);
29346     }
29347     
29348     
29349 });/**
29350  * @class Roo.bootstrap.Alert
29351  * @extends Roo.bootstrap.Component
29352  * Bootstrap Alert class - shows an alert area box
29353  * eg
29354  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29355   Enter a valid email address
29356 </div>
29357  * @licence LGPL
29358  * @cfg {String} title The title of alert
29359  * @cfg {String} html The content of alert
29360  * @cfg {String} weight (  success | info | warning | danger )
29361  * @cfg {String} faicon font-awesomeicon
29362  * 
29363  * @constructor
29364  * Create a new alert
29365  * @param {Object} config The config object
29366  */
29367
29368
29369 Roo.bootstrap.Alert = function(config){
29370     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29371     
29372 };
29373
29374 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29375     
29376     title: '',
29377     html: '',
29378     weight: false,
29379     faicon: false,
29380     
29381     getAutoCreate : function()
29382     {
29383         
29384         var cfg = {
29385             tag : 'div',
29386             cls : 'alert',
29387             cn : [
29388                 {
29389                     tag : 'i',
29390                     cls : 'roo-alert-icon'
29391                     
29392                 },
29393                 {
29394                     tag : 'b',
29395                     cls : 'roo-alert-title',
29396                     html : this.title
29397                 },
29398                 {
29399                     tag : 'span',
29400                     cls : 'roo-alert-text',
29401                     html : this.html
29402                 }
29403             ]
29404         };
29405         
29406         if(this.faicon){
29407             cfg.cn[0].cls += ' fa ' + this.faicon;
29408         }
29409         
29410         if(this.weight){
29411             cfg.cls += ' alert-' + this.weight;
29412         }
29413         
29414         return cfg;
29415     },
29416     
29417     initEvents: function() 
29418     {
29419         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29420     },
29421     
29422     setTitle : function(str)
29423     {
29424         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29425     },
29426     
29427     setText : function(str)
29428     {
29429         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29430     },
29431     
29432     setWeight : function(weight)
29433     {
29434         if(this.weight){
29435             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29436         }
29437         
29438         this.weight = weight;
29439         
29440         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29441     },
29442     
29443     setIcon : function(icon)
29444     {
29445         if(this.faicon){
29446             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29447         }
29448         
29449         this.faicon = icon;
29450         
29451         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29452     },
29453     
29454     hide: function() 
29455     {
29456         this.el.hide();   
29457     },
29458     
29459     show: function() 
29460     {  
29461         this.el.show();   
29462     }
29463     
29464 });
29465
29466  
29467 /*
29468 * Licence: LGPL
29469 */
29470
29471 /**
29472  * @class Roo.bootstrap.UploadCropbox
29473  * @extends Roo.bootstrap.Component
29474  * Bootstrap UploadCropbox class
29475  * @cfg {String} emptyText show when image has been loaded
29476  * @cfg {String} rotateNotify show when image too small to rotate
29477  * @cfg {Number} errorTimeout default 3000
29478  * @cfg {Number} minWidth default 300
29479  * @cfg {Number} minHeight default 300
29480  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29481  * @cfg {Boolean} isDocument (true|false) default false
29482  * @cfg {String} url action url
29483  * @cfg {String} paramName default 'imageUpload'
29484  * @cfg {String} method default POST
29485  * @cfg {Boolean} loadMask (true|false) default true
29486  * @cfg {Boolean} loadingText default 'Loading...'
29487  * 
29488  * @constructor
29489  * Create a new UploadCropbox
29490  * @param {Object} config The config object
29491  */
29492
29493 Roo.bootstrap.UploadCropbox = function(config){
29494     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29495     
29496     this.addEvents({
29497         /**
29498          * @event beforeselectfile
29499          * Fire before select file
29500          * @param {Roo.bootstrap.UploadCropbox} this
29501          */
29502         "beforeselectfile" : true,
29503         /**
29504          * @event initial
29505          * Fire after initEvent
29506          * @param {Roo.bootstrap.UploadCropbox} this
29507          */
29508         "initial" : true,
29509         /**
29510          * @event crop
29511          * Fire after initEvent
29512          * @param {Roo.bootstrap.UploadCropbox} this
29513          * @param {String} data
29514          */
29515         "crop" : true,
29516         /**
29517          * @event prepare
29518          * Fire when preparing the file data
29519          * @param {Roo.bootstrap.UploadCropbox} this
29520          * @param {Object} file
29521          */
29522         "prepare" : true,
29523         /**
29524          * @event exception
29525          * Fire when get exception
29526          * @param {Roo.bootstrap.UploadCropbox} this
29527          * @param {XMLHttpRequest} xhr
29528          */
29529         "exception" : true,
29530         /**
29531          * @event beforeloadcanvas
29532          * Fire before load the canvas
29533          * @param {Roo.bootstrap.UploadCropbox} this
29534          * @param {String} src
29535          */
29536         "beforeloadcanvas" : true,
29537         /**
29538          * @event trash
29539          * Fire when trash image
29540          * @param {Roo.bootstrap.UploadCropbox} this
29541          */
29542         "trash" : true,
29543         /**
29544          * @event download
29545          * Fire when download the image
29546          * @param {Roo.bootstrap.UploadCropbox} this
29547          */
29548         "download" : true,
29549         /**
29550          * @event footerbuttonclick
29551          * Fire when footerbuttonclick
29552          * @param {Roo.bootstrap.UploadCropbox} this
29553          * @param {String} type
29554          */
29555         "footerbuttonclick" : true,
29556         /**
29557          * @event resize
29558          * Fire when resize
29559          * @param {Roo.bootstrap.UploadCropbox} this
29560          */
29561         "resize" : true,
29562         /**
29563          * @event rotate
29564          * Fire when rotate the image
29565          * @param {Roo.bootstrap.UploadCropbox} this
29566          * @param {String} pos
29567          */
29568         "rotate" : true,
29569         /**
29570          * @event inspect
29571          * Fire when inspect the file
29572          * @param {Roo.bootstrap.UploadCropbox} this
29573          * @param {Object} file
29574          */
29575         "inspect" : true,
29576         /**
29577          * @event upload
29578          * Fire when xhr upload the file
29579          * @param {Roo.bootstrap.UploadCropbox} this
29580          * @param {Object} data
29581          */
29582         "upload" : true,
29583         /**
29584          * @event arrange
29585          * Fire when arrange the file data
29586          * @param {Roo.bootstrap.UploadCropbox} this
29587          * @param {Object} formData
29588          */
29589         "arrange" : true
29590     });
29591     
29592     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29593 };
29594
29595 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29596     
29597     emptyText : 'Click to upload image',
29598     rotateNotify : 'Image is too small to rotate',
29599     errorTimeout : 3000,
29600     scale : 0,
29601     baseScale : 1,
29602     rotate : 0,
29603     dragable : false,
29604     pinching : false,
29605     mouseX : 0,
29606     mouseY : 0,
29607     cropData : false,
29608     minWidth : 300,
29609     minHeight : 300,
29610     file : false,
29611     exif : {},
29612     baseRotate : 1,
29613     cropType : 'image/jpeg',
29614     buttons : false,
29615     canvasLoaded : false,
29616     isDocument : false,
29617     method : 'POST',
29618     paramName : 'imageUpload',
29619     loadMask : true,
29620     loadingText : 'Loading...',
29621     maskEl : false,
29622     
29623     getAutoCreate : function()
29624     {
29625         var cfg = {
29626             tag : 'div',
29627             cls : 'roo-upload-cropbox',
29628             cn : [
29629                 {
29630                     tag : 'input',
29631                     cls : 'roo-upload-cropbox-selector',
29632                     type : 'file'
29633                 },
29634                 {
29635                     tag : 'div',
29636                     cls : 'roo-upload-cropbox-body',
29637                     style : 'cursor:pointer',
29638                     cn : [
29639                         {
29640                             tag : 'div',
29641                             cls : 'roo-upload-cropbox-preview'
29642                         },
29643                         {
29644                             tag : 'div',
29645                             cls : 'roo-upload-cropbox-thumb'
29646                         },
29647                         {
29648                             tag : 'div',
29649                             cls : 'roo-upload-cropbox-empty-notify',
29650                             html : this.emptyText
29651                         },
29652                         {
29653                             tag : 'div',
29654                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29655                             html : this.rotateNotify
29656                         }
29657                     ]
29658                 },
29659                 {
29660                     tag : 'div',
29661                     cls : 'roo-upload-cropbox-footer',
29662                     cn : {
29663                         tag : 'div',
29664                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29665                         cn : []
29666                     }
29667                 }
29668             ]
29669         };
29670         
29671         return cfg;
29672     },
29673     
29674     onRender : function(ct, position)
29675     {
29676         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29677         
29678         if (this.buttons.length) {
29679             
29680             Roo.each(this.buttons, function(bb) {
29681                 
29682                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29683                 
29684                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29685                 
29686             }, this);
29687         }
29688         
29689         if(this.loadMask){
29690             this.maskEl = this.el;
29691         }
29692     },
29693     
29694     initEvents : function()
29695     {
29696         this.urlAPI = (window.createObjectURL && window) || 
29697                                 (window.URL && URL.revokeObjectURL && URL) || 
29698                                 (window.webkitURL && webkitURL);
29699                         
29700         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29701         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29702         
29703         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29704         this.selectorEl.hide();
29705         
29706         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29707         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29708         
29709         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29710         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29711         this.thumbEl.hide();
29712         
29713         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29714         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29715         
29716         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29717         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29718         this.errorEl.hide();
29719         
29720         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29721         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29722         this.footerEl.hide();
29723         
29724         this.setThumbBoxSize();
29725         
29726         this.bind();
29727         
29728         this.resize();
29729         
29730         this.fireEvent('initial', this);
29731     },
29732
29733     bind : function()
29734     {
29735         var _this = this;
29736         
29737         window.addEventListener("resize", function() { _this.resize(); } );
29738         
29739         this.bodyEl.on('click', this.beforeSelectFile, this);
29740         
29741         if(Roo.isTouch){
29742             this.bodyEl.on('touchstart', this.onTouchStart, this);
29743             this.bodyEl.on('touchmove', this.onTouchMove, this);
29744             this.bodyEl.on('touchend', this.onTouchEnd, this);
29745         }
29746         
29747         if(!Roo.isTouch){
29748             this.bodyEl.on('mousedown', this.onMouseDown, this);
29749             this.bodyEl.on('mousemove', this.onMouseMove, this);
29750             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29751             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29752             Roo.get(document).on('mouseup', this.onMouseUp, this);
29753         }
29754         
29755         this.selectorEl.on('change', this.onFileSelected, this);
29756     },
29757     
29758     reset : function()
29759     {    
29760         this.scale = 0;
29761         this.baseScale = 1;
29762         this.rotate = 0;
29763         this.baseRotate = 1;
29764         this.dragable = false;
29765         this.pinching = false;
29766         this.mouseX = 0;
29767         this.mouseY = 0;
29768         this.cropData = false;
29769         this.notifyEl.dom.innerHTML = this.emptyText;
29770         
29771         this.selectorEl.dom.value = '';
29772         
29773     },
29774     
29775     resize : function()
29776     {
29777         if(this.fireEvent('resize', this) != false){
29778             this.setThumbBoxPosition();
29779             this.setCanvasPosition();
29780         }
29781     },
29782     
29783     onFooterButtonClick : function(e, el, o, type)
29784     {
29785         switch (type) {
29786             case 'rotate-left' :
29787                 this.onRotateLeft(e);
29788                 break;
29789             case 'rotate-right' :
29790                 this.onRotateRight(e);
29791                 break;
29792             case 'picture' :
29793                 this.beforeSelectFile(e);
29794                 break;
29795             case 'trash' :
29796                 this.trash(e);
29797                 break;
29798             case 'crop' :
29799                 this.crop(e);
29800                 break;
29801             case 'download' :
29802                 this.download(e);
29803                 break;
29804             default :
29805                 break;
29806         }
29807         
29808         this.fireEvent('footerbuttonclick', this, type);
29809     },
29810     
29811     beforeSelectFile : function(e)
29812     {
29813         e.preventDefault();
29814         
29815         if(this.fireEvent('beforeselectfile', this) != false){
29816             this.selectorEl.dom.click();
29817         }
29818     },
29819     
29820     onFileSelected : function(e)
29821     {
29822         e.preventDefault();
29823         
29824         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29825             return;
29826         }
29827         
29828         var file = this.selectorEl.dom.files[0];
29829         
29830         if(this.fireEvent('inspect', this, file) != false){
29831             this.prepare(file);
29832         }
29833         
29834     },
29835     
29836     trash : function(e)
29837     {
29838         this.fireEvent('trash', this);
29839     },
29840     
29841     download : function(e)
29842     {
29843         this.fireEvent('download', this);
29844     },
29845     
29846     loadCanvas : function(src)
29847     {   
29848         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29849             
29850             this.reset();
29851             
29852             this.imageEl = document.createElement('img');
29853             
29854             var _this = this;
29855             
29856             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29857             
29858             this.imageEl.src = src;
29859         }
29860     },
29861     
29862     onLoadCanvas : function()
29863     {   
29864         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29865         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29866         
29867         this.bodyEl.un('click', this.beforeSelectFile, this);
29868         
29869         this.notifyEl.hide();
29870         this.thumbEl.show();
29871         this.footerEl.show();
29872         
29873         this.baseRotateLevel();
29874         
29875         if(this.isDocument){
29876             this.setThumbBoxSize();
29877         }
29878         
29879         this.setThumbBoxPosition();
29880         
29881         this.baseScaleLevel();
29882         
29883         this.draw();
29884         
29885         this.resize();
29886         
29887         this.canvasLoaded = true;
29888         
29889         if(this.loadMask){
29890             this.maskEl.unmask();
29891         }
29892         
29893     },
29894     
29895     setCanvasPosition : function()
29896     {   
29897         if(!this.canvasEl){
29898             return;
29899         }
29900         
29901         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29902         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29903         
29904         this.previewEl.setLeft(pw);
29905         this.previewEl.setTop(ph);
29906         
29907     },
29908     
29909     onMouseDown : function(e)
29910     {   
29911         e.stopEvent();
29912         
29913         this.dragable = true;
29914         this.pinching = false;
29915         
29916         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29917             this.dragable = false;
29918             return;
29919         }
29920         
29921         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29922         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29923         
29924     },
29925     
29926     onMouseMove : function(e)
29927     {   
29928         e.stopEvent();
29929         
29930         if(!this.canvasLoaded){
29931             return;
29932         }
29933         
29934         if (!this.dragable){
29935             return;
29936         }
29937         
29938         var minX = Math.ceil(this.thumbEl.getLeft(true));
29939         var minY = Math.ceil(this.thumbEl.getTop(true));
29940         
29941         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29942         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29943         
29944         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29945         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29946         
29947         x = x - this.mouseX;
29948         y = y - this.mouseY;
29949         
29950         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29951         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29952         
29953         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29954         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29955         
29956         this.previewEl.setLeft(bgX);
29957         this.previewEl.setTop(bgY);
29958         
29959         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29960         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29961     },
29962     
29963     onMouseUp : function(e)
29964     {   
29965         e.stopEvent();
29966         
29967         this.dragable = false;
29968     },
29969     
29970     onMouseWheel : function(e)
29971     {   
29972         e.stopEvent();
29973         
29974         this.startScale = this.scale;
29975         
29976         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29977         
29978         if(!this.zoomable()){
29979             this.scale = this.startScale;
29980             return;
29981         }
29982         
29983         this.draw();
29984         
29985         return;
29986     },
29987     
29988     zoomable : function()
29989     {
29990         var minScale = this.thumbEl.getWidth() / this.minWidth;
29991         
29992         if(this.minWidth < this.minHeight){
29993             minScale = this.thumbEl.getHeight() / this.minHeight;
29994         }
29995         
29996         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29997         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29998         
29999         if(
30000                 this.isDocument &&
30001                 (this.rotate == 0 || this.rotate == 180) && 
30002                 (
30003                     width > this.imageEl.OriginWidth || 
30004                     height > this.imageEl.OriginHeight ||
30005                     (width < this.minWidth && height < this.minHeight)
30006                 )
30007         ){
30008             return false;
30009         }
30010         
30011         if(
30012                 this.isDocument &&
30013                 (this.rotate == 90 || this.rotate == 270) && 
30014                 (
30015                     width > this.imageEl.OriginWidth || 
30016                     height > this.imageEl.OriginHeight ||
30017                     (width < this.minHeight && height < this.minWidth)
30018                 )
30019         ){
30020             return false;
30021         }
30022         
30023         if(
30024                 !this.isDocument &&
30025                 (this.rotate == 0 || this.rotate == 180) && 
30026                 (
30027                     width < this.minWidth || 
30028                     width > this.imageEl.OriginWidth || 
30029                     height < this.minHeight || 
30030                     height > this.imageEl.OriginHeight
30031                 )
30032         ){
30033             return false;
30034         }
30035         
30036         if(
30037                 !this.isDocument &&
30038                 (this.rotate == 90 || this.rotate == 270) && 
30039                 (
30040                     width < this.minHeight || 
30041                     width > this.imageEl.OriginWidth || 
30042                     height < this.minWidth || 
30043                     height > this.imageEl.OriginHeight
30044                 )
30045         ){
30046             return false;
30047         }
30048         
30049         return true;
30050         
30051     },
30052     
30053     onRotateLeft : function(e)
30054     {   
30055         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30056             
30057             var minScale = this.thumbEl.getWidth() / this.minWidth;
30058             
30059             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30060             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30061             
30062             this.startScale = this.scale;
30063             
30064             while (this.getScaleLevel() < minScale){
30065             
30066                 this.scale = this.scale + 1;
30067                 
30068                 if(!this.zoomable()){
30069                     break;
30070                 }
30071                 
30072                 if(
30073                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30074                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30075                 ){
30076                     continue;
30077                 }
30078                 
30079                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30080
30081                 this.draw();
30082                 
30083                 return;
30084             }
30085             
30086             this.scale = this.startScale;
30087             
30088             this.onRotateFail();
30089             
30090             return false;
30091         }
30092         
30093         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30094
30095         if(this.isDocument){
30096             this.setThumbBoxSize();
30097             this.setThumbBoxPosition();
30098             this.setCanvasPosition();
30099         }
30100         
30101         this.draw();
30102         
30103         this.fireEvent('rotate', this, 'left');
30104         
30105     },
30106     
30107     onRotateRight : function(e)
30108     {
30109         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30110             
30111             var minScale = this.thumbEl.getWidth() / this.minWidth;
30112         
30113             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30114             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30115             
30116             this.startScale = this.scale;
30117             
30118             while (this.getScaleLevel() < minScale){
30119             
30120                 this.scale = this.scale + 1;
30121                 
30122                 if(!this.zoomable()){
30123                     break;
30124                 }
30125                 
30126                 if(
30127                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30128                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30129                 ){
30130                     continue;
30131                 }
30132                 
30133                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30134
30135                 this.draw();
30136                 
30137                 return;
30138             }
30139             
30140             this.scale = this.startScale;
30141             
30142             this.onRotateFail();
30143             
30144             return false;
30145         }
30146         
30147         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30148
30149         if(this.isDocument){
30150             this.setThumbBoxSize();
30151             this.setThumbBoxPosition();
30152             this.setCanvasPosition();
30153         }
30154         
30155         this.draw();
30156         
30157         this.fireEvent('rotate', this, 'right');
30158     },
30159     
30160     onRotateFail : function()
30161     {
30162         this.errorEl.show(true);
30163         
30164         var _this = this;
30165         
30166         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30167     },
30168     
30169     draw : function()
30170     {
30171         this.previewEl.dom.innerHTML = '';
30172         
30173         var canvasEl = document.createElement("canvas");
30174         
30175         var contextEl = canvasEl.getContext("2d");
30176         
30177         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30178         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30179         var center = this.imageEl.OriginWidth / 2;
30180         
30181         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30182             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30183             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30184             center = this.imageEl.OriginHeight / 2;
30185         }
30186         
30187         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30188         
30189         contextEl.translate(center, center);
30190         contextEl.rotate(this.rotate * Math.PI / 180);
30191
30192         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30193         
30194         this.canvasEl = document.createElement("canvas");
30195         
30196         this.contextEl = this.canvasEl.getContext("2d");
30197         
30198         switch (this.rotate) {
30199             case 0 :
30200                 
30201                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30202                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
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 90 : 
30208                 
30209                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30210                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30211                 
30212                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30213                     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);
30214                     break;
30215                 }
30216                 
30217                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30218                 
30219                 break;
30220             case 180 :
30221                 
30222                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30223                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30224                 
30225                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30226                     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);
30227                     break;
30228                 }
30229                 
30230                 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);
30231                 
30232                 break;
30233             case 270 :
30234                 
30235                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30236                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30237         
30238                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30239                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30240                     break;
30241                 }
30242                 
30243                 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);
30244                 
30245                 break;
30246             default : 
30247                 break;
30248         }
30249         
30250         this.previewEl.appendChild(this.canvasEl);
30251         
30252         this.setCanvasPosition();
30253     },
30254     
30255     crop : function()
30256     {
30257         if(!this.canvasLoaded){
30258             return;
30259         }
30260         
30261         var imageCanvas = document.createElement("canvas");
30262         
30263         var imageContext = imageCanvas.getContext("2d");
30264         
30265         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30266         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30267         
30268         var center = imageCanvas.width / 2;
30269         
30270         imageContext.translate(center, center);
30271         
30272         imageContext.rotate(this.rotate * Math.PI / 180);
30273         
30274         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30275         
30276         var canvas = document.createElement("canvas");
30277         
30278         var context = canvas.getContext("2d");
30279                 
30280         canvas.width = this.minWidth;
30281         canvas.height = this.minHeight;
30282
30283         switch (this.rotate) {
30284             case 0 :
30285                 
30286                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30287                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30288                 
30289                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30290                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30291                 
30292                 var targetWidth = this.minWidth - 2 * x;
30293                 var targetHeight = this.minHeight - 2 * y;
30294                 
30295                 var scale = 1;
30296                 
30297                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30298                     scale = targetWidth / width;
30299                 }
30300                 
30301                 if(x > 0 && y == 0){
30302                     scale = targetHeight / height;
30303                 }
30304                 
30305                 if(x > 0 && y > 0){
30306                     scale = targetWidth / width;
30307                     
30308                     if(width < height){
30309                         scale = targetHeight / height;
30310                     }
30311                 }
30312                 
30313                 context.scale(scale, scale);
30314                 
30315                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30316                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30317
30318                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30319                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30320
30321                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30322                 
30323                 break;
30324             case 90 : 
30325                 
30326                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30327                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30328                 
30329                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30330                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30331                 
30332                 var targetWidth = this.minWidth - 2 * x;
30333                 var targetHeight = this.minHeight - 2 * y;
30334                 
30335                 var scale = 1;
30336                 
30337                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30338                     scale = targetWidth / width;
30339                 }
30340                 
30341                 if(x > 0 && y == 0){
30342                     scale = targetHeight / height;
30343                 }
30344                 
30345                 if(x > 0 && y > 0){
30346                     scale = targetWidth / width;
30347                     
30348                     if(width < height){
30349                         scale = targetHeight / height;
30350                     }
30351                 }
30352                 
30353                 context.scale(scale, scale);
30354                 
30355                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30356                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30357
30358                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30359                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30360                 
30361                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30362                 
30363                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30364                 
30365                 break;
30366             case 180 :
30367                 
30368                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30369                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30370                 
30371                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30372                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30373                 
30374                 var targetWidth = this.minWidth - 2 * x;
30375                 var targetHeight = this.minHeight - 2 * y;
30376                 
30377                 var scale = 1;
30378                 
30379                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30380                     scale = targetWidth / width;
30381                 }
30382                 
30383                 if(x > 0 && y == 0){
30384                     scale = targetHeight / height;
30385                 }
30386                 
30387                 if(x > 0 && y > 0){
30388                     scale = targetWidth / width;
30389                     
30390                     if(width < height){
30391                         scale = targetHeight / height;
30392                     }
30393                 }
30394                 
30395                 context.scale(scale, scale);
30396                 
30397                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30398                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30399
30400                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30401                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30402
30403                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30404                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30405                 
30406                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30407                 
30408                 break;
30409             case 270 :
30410                 
30411                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30412                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30413                 
30414                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30415                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30416                 
30417                 var targetWidth = this.minWidth - 2 * x;
30418                 var targetHeight = this.minHeight - 2 * y;
30419                 
30420                 var scale = 1;
30421                 
30422                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30423                     scale = targetWidth / width;
30424                 }
30425                 
30426                 if(x > 0 && y == 0){
30427                     scale = targetHeight / height;
30428                 }
30429                 
30430                 if(x > 0 && y > 0){
30431                     scale = targetWidth / width;
30432                     
30433                     if(width < height){
30434                         scale = targetHeight / height;
30435                     }
30436                 }
30437                 
30438                 context.scale(scale, scale);
30439                 
30440                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30441                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30442
30443                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30444                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30445                 
30446                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30447                 
30448                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30449                 
30450                 break;
30451             default : 
30452                 break;
30453         }
30454         
30455         this.cropData = canvas.toDataURL(this.cropType);
30456         
30457         if(this.fireEvent('crop', this, this.cropData) !== false){
30458             this.process(this.file, this.cropData);
30459         }
30460         
30461         return;
30462         
30463     },
30464     
30465     setThumbBoxSize : function()
30466     {
30467         var width, height;
30468         
30469         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30470             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30471             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30472             
30473             this.minWidth = width;
30474             this.minHeight = height;
30475             
30476             if(this.rotate == 90 || this.rotate == 270){
30477                 this.minWidth = height;
30478                 this.minHeight = width;
30479             }
30480         }
30481         
30482         height = 300;
30483         width = Math.ceil(this.minWidth * height / this.minHeight);
30484         
30485         if(this.minWidth > this.minHeight){
30486             width = 300;
30487             height = Math.ceil(this.minHeight * width / this.minWidth);
30488         }
30489         
30490         this.thumbEl.setStyle({
30491             width : width + 'px',
30492             height : height + 'px'
30493         });
30494
30495         return;
30496             
30497     },
30498     
30499     setThumbBoxPosition : function()
30500     {
30501         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30502         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30503         
30504         this.thumbEl.setLeft(x);
30505         this.thumbEl.setTop(y);
30506         
30507     },
30508     
30509     baseRotateLevel : function()
30510     {
30511         this.baseRotate = 1;
30512         
30513         if(
30514                 typeof(this.exif) != 'undefined' &&
30515                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30516                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30517         ){
30518             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30519         }
30520         
30521         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30522         
30523     },
30524     
30525     baseScaleLevel : function()
30526     {
30527         var width, height;
30528         
30529         if(this.isDocument){
30530             
30531             if(this.baseRotate == 6 || this.baseRotate == 8){
30532             
30533                 height = this.thumbEl.getHeight();
30534                 this.baseScale = height / this.imageEl.OriginWidth;
30535
30536                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30537                     width = this.thumbEl.getWidth();
30538                     this.baseScale = width / this.imageEl.OriginHeight;
30539                 }
30540
30541                 return;
30542             }
30543
30544             height = this.thumbEl.getHeight();
30545             this.baseScale = height / this.imageEl.OriginHeight;
30546
30547             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30548                 width = this.thumbEl.getWidth();
30549                 this.baseScale = width / this.imageEl.OriginWidth;
30550             }
30551
30552             return;
30553         }
30554         
30555         if(this.baseRotate == 6 || this.baseRotate == 8){
30556             
30557             width = this.thumbEl.getHeight();
30558             this.baseScale = width / this.imageEl.OriginHeight;
30559             
30560             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30561                 height = this.thumbEl.getWidth();
30562                 this.baseScale = height / this.imageEl.OriginHeight;
30563             }
30564             
30565             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30566                 height = this.thumbEl.getWidth();
30567                 this.baseScale = height / this.imageEl.OriginHeight;
30568                 
30569                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30570                     width = this.thumbEl.getHeight();
30571                     this.baseScale = width / this.imageEl.OriginWidth;
30572                 }
30573             }
30574             
30575             return;
30576         }
30577         
30578         width = this.thumbEl.getWidth();
30579         this.baseScale = width / this.imageEl.OriginWidth;
30580         
30581         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30582             height = this.thumbEl.getHeight();
30583             this.baseScale = height / this.imageEl.OriginHeight;
30584         }
30585         
30586         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30587             
30588             height = this.thumbEl.getHeight();
30589             this.baseScale = height / this.imageEl.OriginHeight;
30590             
30591             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30592                 width = this.thumbEl.getWidth();
30593                 this.baseScale = width / this.imageEl.OriginWidth;
30594             }
30595             
30596         }
30597         
30598         return;
30599     },
30600     
30601     getScaleLevel : function()
30602     {
30603         return this.baseScale * Math.pow(1.1, this.scale);
30604     },
30605     
30606     onTouchStart : function(e)
30607     {
30608         if(!this.canvasLoaded){
30609             this.beforeSelectFile(e);
30610             return;
30611         }
30612         
30613         var touches = e.browserEvent.touches;
30614         
30615         if(!touches){
30616             return;
30617         }
30618         
30619         if(touches.length == 1){
30620             this.onMouseDown(e);
30621             return;
30622         }
30623         
30624         if(touches.length != 2){
30625             return;
30626         }
30627         
30628         var coords = [];
30629         
30630         for(var i = 0, finger; finger = touches[i]; i++){
30631             coords.push(finger.pageX, finger.pageY);
30632         }
30633         
30634         var x = Math.pow(coords[0] - coords[2], 2);
30635         var y = Math.pow(coords[1] - coords[3], 2);
30636         
30637         this.startDistance = Math.sqrt(x + y);
30638         
30639         this.startScale = this.scale;
30640         
30641         this.pinching = true;
30642         this.dragable = false;
30643         
30644     },
30645     
30646     onTouchMove : function(e)
30647     {
30648         if(!this.pinching && !this.dragable){
30649             return;
30650         }
30651         
30652         var touches = e.browserEvent.touches;
30653         
30654         if(!touches){
30655             return;
30656         }
30657         
30658         if(this.dragable){
30659             this.onMouseMove(e);
30660             return;
30661         }
30662         
30663         var coords = [];
30664         
30665         for(var i = 0, finger; finger = touches[i]; i++){
30666             coords.push(finger.pageX, finger.pageY);
30667         }
30668         
30669         var x = Math.pow(coords[0] - coords[2], 2);
30670         var y = Math.pow(coords[1] - coords[3], 2);
30671         
30672         this.endDistance = Math.sqrt(x + y);
30673         
30674         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30675         
30676         if(!this.zoomable()){
30677             this.scale = this.startScale;
30678             return;
30679         }
30680         
30681         this.draw();
30682         
30683     },
30684     
30685     onTouchEnd : function(e)
30686     {
30687         this.pinching = false;
30688         this.dragable = false;
30689         
30690     },
30691     
30692     process : function(file, crop)
30693     {
30694         if(this.loadMask){
30695             this.maskEl.mask(this.loadingText);
30696         }
30697         
30698         this.xhr = new XMLHttpRequest();
30699         
30700         file.xhr = this.xhr;
30701
30702         this.xhr.open(this.method, this.url, true);
30703         
30704         var headers = {
30705             "Accept": "application/json",
30706             "Cache-Control": "no-cache",
30707             "X-Requested-With": "XMLHttpRequest"
30708         };
30709         
30710         for (var headerName in headers) {
30711             var headerValue = headers[headerName];
30712             if (headerValue) {
30713                 this.xhr.setRequestHeader(headerName, headerValue);
30714             }
30715         }
30716         
30717         var _this = this;
30718         
30719         this.xhr.onload = function()
30720         {
30721             _this.xhrOnLoad(_this.xhr);
30722         }
30723         
30724         this.xhr.onerror = function()
30725         {
30726             _this.xhrOnError(_this.xhr);
30727         }
30728         
30729         var formData = new FormData();
30730
30731         formData.append('returnHTML', 'NO');
30732         
30733         if(crop){
30734             formData.append('crop', crop);
30735         }
30736         
30737         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30738             formData.append(this.paramName, file, file.name);
30739         }
30740         
30741         if(typeof(file.filename) != 'undefined'){
30742             formData.append('filename', file.filename);
30743         }
30744         
30745         if(typeof(file.mimetype) != 'undefined'){
30746             formData.append('mimetype', file.mimetype);
30747         }
30748         
30749         if(this.fireEvent('arrange', this, formData) != false){
30750             this.xhr.send(formData);
30751         };
30752     },
30753     
30754     xhrOnLoad : function(xhr)
30755     {
30756         if(this.loadMask){
30757             this.maskEl.unmask();
30758         }
30759         
30760         if (xhr.readyState !== 4) {
30761             this.fireEvent('exception', this, xhr);
30762             return;
30763         }
30764
30765         var response = Roo.decode(xhr.responseText);
30766         
30767         if(!response.success){
30768             this.fireEvent('exception', this, xhr);
30769             return;
30770         }
30771         
30772         var response = Roo.decode(xhr.responseText);
30773         
30774         this.fireEvent('upload', this, response);
30775         
30776     },
30777     
30778     xhrOnError : function()
30779     {
30780         if(this.loadMask){
30781             this.maskEl.unmask();
30782         }
30783         
30784         Roo.log('xhr on error');
30785         
30786         var response = Roo.decode(xhr.responseText);
30787           
30788         Roo.log(response);
30789         
30790     },
30791     
30792     prepare : function(file)
30793     {   
30794         if(this.loadMask){
30795             this.maskEl.mask(this.loadingText);
30796         }
30797         
30798         this.file = false;
30799         this.exif = {};
30800         
30801         if(typeof(file) === 'string'){
30802             this.loadCanvas(file);
30803             return;
30804         }
30805         
30806         if(!file || !this.urlAPI){
30807             return;
30808         }
30809         
30810         this.file = file;
30811         this.cropType = file.type;
30812         
30813         var _this = this;
30814         
30815         if(this.fireEvent('prepare', this, this.file) != false){
30816             
30817             var reader = new FileReader();
30818             
30819             reader.onload = function (e) {
30820                 if (e.target.error) {
30821                     Roo.log(e.target.error);
30822                     return;
30823                 }
30824                 
30825                 var buffer = e.target.result,
30826                     dataView = new DataView(buffer),
30827                     offset = 2,
30828                     maxOffset = dataView.byteLength - 4,
30829                     markerBytes,
30830                     markerLength;
30831                 
30832                 if (dataView.getUint16(0) === 0xffd8) {
30833                     while (offset < maxOffset) {
30834                         markerBytes = dataView.getUint16(offset);
30835                         
30836                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30837                             markerLength = dataView.getUint16(offset + 2) + 2;
30838                             if (offset + markerLength > dataView.byteLength) {
30839                                 Roo.log('Invalid meta data: Invalid segment size.');
30840                                 break;
30841                             }
30842                             
30843                             if(markerBytes == 0xffe1){
30844                                 _this.parseExifData(
30845                                     dataView,
30846                                     offset,
30847                                     markerLength
30848                                 );
30849                             }
30850                             
30851                             offset += markerLength;
30852                             
30853                             continue;
30854                         }
30855                         
30856                         break;
30857                     }
30858                     
30859                 }
30860                 
30861                 var url = _this.urlAPI.createObjectURL(_this.file);
30862                 
30863                 _this.loadCanvas(url);
30864                 
30865                 return;
30866             }
30867             
30868             reader.readAsArrayBuffer(this.file);
30869             
30870         }
30871         
30872     },
30873     
30874     parseExifData : function(dataView, offset, length)
30875     {
30876         var tiffOffset = offset + 10,
30877             littleEndian,
30878             dirOffset;
30879     
30880         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30881             // No Exif data, might be XMP data instead
30882             return;
30883         }
30884         
30885         // Check for the ASCII code for "Exif" (0x45786966):
30886         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30887             // No Exif data, might be XMP data instead
30888             return;
30889         }
30890         if (tiffOffset + 8 > dataView.byteLength) {
30891             Roo.log('Invalid Exif data: Invalid segment size.');
30892             return;
30893         }
30894         // Check for the two null bytes:
30895         if (dataView.getUint16(offset + 8) !== 0x0000) {
30896             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30897             return;
30898         }
30899         // Check the byte alignment:
30900         switch (dataView.getUint16(tiffOffset)) {
30901         case 0x4949:
30902             littleEndian = true;
30903             break;
30904         case 0x4D4D:
30905             littleEndian = false;
30906             break;
30907         default:
30908             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30909             return;
30910         }
30911         // Check for the TIFF tag marker (0x002A):
30912         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30913             Roo.log('Invalid Exif data: Missing TIFF marker.');
30914             return;
30915         }
30916         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30917         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30918         
30919         this.parseExifTags(
30920             dataView,
30921             tiffOffset,
30922             tiffOffset + dirOffset,
30923             littleEndian
30924         );
30925     },
30926     
30927     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30928     {
30929         var tagsNumber,
30930             dirEndOffset,
30931             i;
30932         if (dirOffset + 6 > dataView.byteLength) {
30933             Roo.log('Invalid Exif data: Invalid directory offset.');
30934             return;
30935         }
30936         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30937         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30938         if (dirEndOffset + 4 > dataView.byteLength) {
30939             Roo.log('Invalid Exif data: Invalid directory size.');
30940             return;
30941         }
30942         for (i = 0; i < tagsNumber; i += 1) {
30943             this.parseExifTag(
30944                 dataView,
30945                 tiffOffset,
30946                 dirOffset + 2 + 12 * i, // tag offset
30947                 littleEndian
30948             );
30949         }
30950         // Return the offset to the next directory:
30951         return dataView.getUint32(dirEndOffset, littleEndian);
30952     },
30953     
30954     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30955     {
30956         var tag = dataView.getUint16(offset, littleEndian);
30957         
30958         this.exif[tag] = this.getExifValue(
30959             dataView,
30960             tiffOffset,
30961             offset,
30962             dataView.getUint16(offset + 2, littleEndian), // tag type
30963             dataView.getUint32(offset + 4, littleEndian), // tag length
30964             littleEndian
30965         );
30966     },
30967     
30968     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30969     {
30970         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30971             tagSize,
30972             dataOffset,
30973             values,
30974             i,
30975             str,
30976             c;
30977     
30978         if (!tagType) {
30979             Roo.log('Invalid Exif data: Invalid tag type.');
30980             return;
30981         }
30982         
30983         tagSize = tagType.size * length;
30984         // Determine if the value is contained in the dataOffset bytes,
30985         // or if the value at the dataOffset is a pointer to the actual data:
30986         dataOffset = tagSize > 4 ?
30987                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30988         if (dataOffset + tagSize > dataView.byteLength) {
30989             Roo.log('Invalid Exif data: Invalid data offset.');
30990             return;
30991         }
30992         if (length === 1) {
30993             return tagType.getValue(dataView, dataOffset, littleEndian);
30994         }
30995         values = [];
30996         for (i = 0; i < length; i += 1) {
30997             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30998         }
30999         
31000         if (tagType.ascii) {
31001             str = '';
31002             // Concatenate the chars:
31003             for (i = 0; i < values.length; i += 1) {
31004                 c = values[i];
31005                 // Ignore the terminating NULL byte(s):
31006                 if (c === '\u0000') {
31007                     break;
31008                 }
31009                 str += c;
31010             }
31011             return str;
31012         }
31013         return values;
31014     }
31015     
31016 });
31017
31018 Roo.apply(Roo.bootstrap.UploadCropbox, {
31019     tags : {
31020         'Orientation': 0x0112
31021     },
31022     
31023     Orientation: {
31024             1: 0, //'top-left',
31025 //            2: 'top-right',
31026             3: 180, //'bottom-right',
31027 //            4: 'bottom-left',
31028 //            5: 'left-top',
31029             6: 90, //'right-top',
31030 //            7: 'right-bottom',
31031             8: 270 //'left-bottom'
31032     },
31033     
31034     exifTagTypes : {
31035         // byte, 8-bit unsigned int:
31036         1: {
31037             getValue: function (dataView, dataOffset) {
31038                 return dataView.getUint8(dataOffset);
31039             },
31040             size: 1
31041         },
31042         // ascii, 8-bit byte:
31043         2: {
31044             getValue: function (dataView, dataOffset) {
31045                 return String.fromCharCode(dataView.getUint8(dataOffset));
31046             },
31047             size: 1,
31048             ascii: true
31049         },
31050         // short, 16 bit int:
31051         3: {
31052             getValue: function (dataView, dataOffset, littleEndian) {
31053                 return dataView.getUint16(dataOffset, littleEndian);
31054             },
31055             size: 2
31056         },
31057         // long, 32 bit int:
31058         4: {
31059             getValue: function (dataView, dataOffset, littleEndian) {
31060                 return dataView.getUint32(dataOffset, littleEndian);
31061             },
31062             size: 4
31063         },
31064         // rational = two long values, first is numerator, second is denominator:
31065         5: {
31066             getValue: function (dataView, dataOffset, littleEndian) {
31067                 return dataView.getUint32(dataOffset, littleEndian) /
31068                     dataView.getUint32(dataOffset + 4, littleEndian);
31069             },
31070             size: 8
31071         },
31072         // slong, 32 bit signed int:
31073         9: {
31074             getValue: function (dataView, dataOffset, littleEndian) {
31075                 return dataView.getInt32(dataOffset, littleEndian);
31076             },
31077             size: 4
31078         },
31079         // srational, two slongs, first is numerator, second is denominator:
31080         10: {
31081             getValue: function (dataView, dataOffset, littleEndian) {
31082                 return dataView.getInt32(dataOffset, littleEndian) /
31083                     dataView.getInt32(dataOffset + 4, littleEndian);
31084             },
31085             size: 8
31086         }
31087     },
31088     
31089     footer : {
31090         STANDARD : [
31091             {
31092                 tag : 'div',
31093                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31094                 action : 'rotate-left',
31095                 cn : [
31096                     {
31097                         tag : 'button',
31098                         cls : 'btn btn-default',
31099                         html : '<i class="fa fa-undo"></i>'
31100                     }
31101                 ]
31102             },
31103             {
31104                 tag : 'div',
31105                 cls : 'btn-group roo-upload-cropbox-picture',
31106                 action : 'picture',
31107                 cn : [
31108                     {
31109                         tag : 'button',
31110                         cls : 'btn btn-default',
31111                         html : '<i class="fa fa-picture-o"></i>'
31112                     }
31113                 ]
31114             },
31115             {
31116                 tag : 'div',
31117                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31118                 action : 'rotate-right',
31119                 cn : [
31120                     {
31121                         tag : 'button',
31122                         cls : 'btn btn-default',
31123                         html : '<i class="fa fa-repeat"></i>'
31124                     }
31125                 ]
31126             }
31127         ],
31128         DOCUMENT : [
31129             {
31130                 tag : 'div',
31131                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31132                 action : 'rotate-left',
31133                 cn : [
31134                     {
31135                         tag : 'button',
31136                         cls : 'btn btn-default',
31137                         html : '<i class="fa fa-undo"></i>'
31138                     }
31139                 ]
31140             },
31141             {
31142                 tag : 'div',
31143                 cls : 'btn-group roo-upload-cropbox-download',
31144                 action : 'download',
31145                 cn : [
31146                     {
31147                         tag : 'button',
31148                         cls : 'btn btn-default',
31149                         html : '<i class="fa fa-download"></i>'
31150                     }
31151                 ]
31152             },
31153             {
31154                 tag : 'div',
31155                 cls : 'btn-group roo-upload-cropbox-crop',
31156                 action : 'crop',
31157                 cn : [
31158                     {
31159                         tag : 'button',
31160                         cls : 'btn btn-default',
31161                         html : '<i class="fa fa-crop"></i>'
31162                     }
31163                 ]
31164             },
31165             {
31166                 tag : 'div',
31167                 cls : 'btn-group roo-upload-cropbox-trash',
31168                 action : 'trash',
31169                 cn : [
31170                     {
31171                         tag : 'button',
31172                         cls : 'btn btn-default',
31173                         html : '<i class="fa fa-trash"></i>'
31174                     }
31175                 ]
31176             },
31177             {
31178                 tag : 'div',
31179                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31180                 action : 'rotate-right',
31181                 cn : [
31182                     {
31183                         tag : 'button',
31184                         cls : 'btn btn-default',
31185                         html : '<i class="fa fa-repeat"></i>'
31186                     }
31187                 ]
31188             }
31189         ],
31190         ROTATOR : [
31191             {
31192                 tag : 'div',
31193                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31194                 action : 'rotate-left',
31195                 cn : [
31196                     {
31197                         tag : 'button',
31198                         cls : 'btn btn-default',
31199                         html : '<i class="fa fa-undo"></i>'
31200                     }
31201                 ]
31202             },
31203             {
31204                 tag : 'div',
31205                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31206                 action : 'rotate-right',
31207                 cn : [
31208                     {
31209                         tag : 'button',
31210                         cls : 'btn btn-default',
31211                         html : '<i class="fa fa-repeat"></i>'
31212                     }
31213                 ]
31214             }
31215         ]
31216     }
31217 });
31218
31219 /*
31220 * Licence: LGPL
31221 */
31222
31223 /**
31224  * @class Roo.bootstrap.DocumentManager
31225  * @extends Roo.bootstrap.Component
31226  * Bootstrap DocumentManager class
31227  * @cfg {String} paramName default 'imageUpload'
31228  * @cfg {String} toolTipName default 'filename'
31229  * @cfg {String} method default POST
31230  * @cfg {String} url action url
31231  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31232  * @cfg {Boolean} multiple multiple upload default true
31233  * @cfg {Number} thumbSize default 300
31234  * @cfg {String} fieldLabel
31235  * @cfg {Number} labelWidth default 4
31236  * @cfg {String} labelAlign (left|top) default left
31237  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31238 * @cfg {Number} labellg set the width of label (1-12)
31239  * @cfg {Number} labelmd set the width of label (1-12)
31240  * @cfg {Number} labelsm set the width of label (1-12)
31241  * @cfg {Number} labelxs set the width of label (1-12)
31242  * 
31243  * @constructor
31244  * Create a new DocumentManager
31245  * @param {Object} config The config object
31246  */
31247
31248 Roo.bootstrap.DocumentManager = function(config){
31249     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31250     
31251     this.files = [];
31252     this.delegates = [];
31253     
31254     this.addEvents({
31255         /**
31256          * @event initial
31257          * Fire when initial the DocumentManager
31258          * @param {Roo.bootstrap.DocumentManager} this
31259          */
31260         "initial" : true,
31261         /**
31262          * @event inspect
31263          * inspect selected file
31264          * @param {Roo.bootstrap.DocumentManager} this
31265          * @param {File} file
31266          */
31267         "inspect" : true,
31268         /**
31269          * @event exception
31270          * Fire when xhr load exception
31271          * @param {Roo.bootstrap.DocumentManager} this
31272          * @param {XMLHttpRequest} xhr
31273          */
31274         "exception" : true,
31275         /**
31276          * @event afterupload
31277          * Fire when xhr load exception
31278          * @param {Roo.bootstrap.DocumentManager} this
31279          * @param {XMLHttpRequest} xhr
31280          */
31281         "afterupload" : true,
31282         /**
31283          * @event prepare
31284          * prepare the form data
31285          * @param {Roo.bootstrap.DocumentManager} this
31286          * @param {Object} formData
31287          */
31288         "prepare" : true,
31289         /**
31290          * @event remove
31291          * Fire when remove the file
31292          * @param {Roo.bootstrap.DocumentManager} this
31293          * @param {Object} file
31294          */
31295         "remove" : true,
31296         /**
31297          * @event refresh
31298          * Fire after refresh the file
31299          * @param {Roo.bootstrap.DocumentManager} this
31300          */
31301         "refresh" : true,
31302         /**
31303          * @event click
31304          * Fire after click the image
31305          * @param {Roo.bootstrap.DocumentManager} this
31306          * @param {Object} file
31307          */
31308         "click" : true,
31309         /**
31310          * @event edit
31311          * Fire when upload a image and editable set to true
31312          * @param {Roo.bootstrap.DocumentManager} this
31313          * @param {Object} file
31314          */
31315         "edit" : true,
31316         /**
31317          * @event beforeselectfile
31318          * Fire before select file
31319          * @param {Roo.bootstrap.DocumentManager} this
31320          */
31321         "beforeselectfile" : true,
31322         /**
31323          * @event process
31324          * Fire before process file
31325          * @param {Roo.bootstrap.DocumentManager} this
31326          * @param {Object} file
31327          */
31328         "process" : true,
31329         /**
31330          * @event previewrendered
31331          * Fire when preview rendered
31332          * @param {Roo.bootstrap.DocumentManager} this
31333          * @param {Object} file
31334          */
31335         "previewrendered" : true,
31336         /**
31337          */
31338         "previewResize" : true
31339         
31340     });
31341 };
31342
31343 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31344     
31345     boxes : 0,
31346     inputName : '',
31347     thumbSize : 300,
31348     multiple : true,
31349     files : false,
31350     method : 'POST',
31351     url : '',
31352     paramName : 'imageUpload',
31353     toolTipName : 'filename',
31354     fieldLabel : '',
31355     labelWidth : 4,
31356     labelAlign : 'left',
31357     editable : true,
31358     delegates : false,
31359     xhr : false, 
31360     
31361     labellg : 0,
31362     labelmd : 0,
31363     labelsm : 0,
31364     labelxs : 0,
31365     
31366     getAutoCreate : function()
31367     {   
31368         var managerWidget = {
31369             tag : 'div',
31370             cls : 'roo-document-manager',
31371             cn : [
31372                 {
31373                     tag : 'input',
31374                     cls : 'roo-document-manager-selector',
31375                     type : 'file'
31376                 },
31377                 {
31378                     tag : 'div',
31379                     cls : 'roo-document-manager-uploader',
31380                     cn : [
31381                         {
31382                             tag : 'div',
31383                             cls : 'roo-document-manager-upload-btn',
31384                             html : '<i class="fa fa-plus"></i>'
31385                         }
31386                     ]
31387                     
31388                 }
31389             ]
31390         };
31391         
31392         var content = [
31393             {
31394                 tag : 'div',
31395                 cls : 'column col-md-12',
31396                 cn : managerWidget
31397             }
31398         ];
31399         
31400         if(this.fieldLabel.length){
31401             
31402             content = [
31403                 {
31404                     tag : 'div',
31405                     cls : 'column col-md-12',
31406                     html : this.fieldLabel
31407                 },
31408                 {
31409                     tag : 'div',
31410                     cls : 'column col-md-12',
31411                     cn : managerWidget
31412                 }
31413             ];
31414
31415             if(this.labelAlign == 'left'){
31416                 content = [
31417                     {
31418                         tag : 'div',
31419                         cls : 'column',
31420                         html : this.fieldLabel
31421                     },
31422                     {
31423                         tag : 'div',
31424                         cls : 'column',
31425                         cn : managerWidget
31426                     }
31427                 ];
31428                 
31429                 if(this.labelWidth > 12){
31430                     content[0].style = "width: " + this.labelWidth + 'px';
31431                 }
31432
31433                 if(this.labelWidth < 13 && this.labelmd == 0){
31434                     this.labelmd = this.labelWidth;
31435                 }
31436
31437                 if(this.labellg > 0){
31438                     content[0].cls += ' col-lg-' + this.labellg;
31439                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31440                 }
31441
31442                 if(this.labelmd > 0){
31443                     content[0].cls += ' col-md-' + this.labelmd;
31444                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31445                 }
31446
31447                 if(this.labelsm > 0){
31448                     content[0].cls += ' col-sm-' + this.labelsm;
31449                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31450                 }
31451
31452                 if(this.labelxs > 0){
31453                     content[0].cls += ' col-xs-' + this.labelxs;
31454                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31455                 }
31456                 
31457             }
31458         }
31459         
31460         var cfg = {
31461             tag : 'div',
31462             cls : 'row clearfix',
31463             cn : content
31464         };
31465         
31466         return cfg;
31467         
31468     },
31469     
31470     initEvents : function()
31471     {
31472         this.managerEl = this.el.select('.roo-document-manager', true).first();
31473         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31474         
31475         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31476         this.selectorEl.hide();
31477         
31478         if(this.multiple){
31479             this.selectorEl.attr('multiple', 'multiple');
31480         }
31481         
31482         this.selectorEl.on('change', this.onFileSelected, this);
31483         
31484         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31485         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31486         
31487         this.uploader.on('click', this.onUploaderClick, this);
31488         
31489         this.renderProgressDialog();
31490         
31491         var _this = this;
31492         
31493         window.addEventListener("resize", function() { _this.refresh(); } );
31494         
31495         this.fireEvent('initial', this);
31496     },
31497     
31498     renderProgressDialog : function()
31499     {
31500         var _this = this;
31501         
31502         this.progressDialog = new Roo.bootstrap.Modal({
31503             cls : 'roo-document-manager-progress-dialog',
31504             allow_close : false,
31505             animate : false,
31506             title : '',
31507             buttons : [
31508                 {
31509                     name  :'cancel',
31510                     weight : 'danger',
31511                     html : 'Cancel'
31512                 }
31513             ], 
31514             listeners : { 
31515                 btnclick : function() {
31516                     _this.uploadCancel();
31517                     this.hide();
31518                 }
31519             }
31520         });
31521          
31522         this.progressDialog.render(Roo.get(document.body));
31523          
31524         this.progress = new Roo.bootstrap.Progress({
31525             cls : 'roo-document-manager-progress',
31526             active : true,
31527             striped : true
31528         });
31529         
31530         this.progress.render(this.progressDialog.getChildContainer());
31531         
31532         this.progressBar = new Roo.bootstrap.ProgressBar({
31533             cls : 'roo-document-manager-progress-bar',
31534             aria_valuenow : 0,
31535             aria_valuemin : 0,
31536             aria_valuemax : 12,
31537             panel : 'success'
31538         });
31539         
31540         this.progressBar.render(this.progress.getChildContainer());
31541     },
31542     
31543     onUploaderClick : function(e)
31544     {
31545         e.preventDefault();
31546      
31547         if(this.fireEvent('beforeselectfile', this) != false){
31548             this.selectorEl.dom.click();
31549         }
31550         
31551     },
31552     
31553     onFileSelected : function(e)
31554     {
31555         e.preventDefault();
31556         
31557         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31558             return;
31559         }
31560         
31561         Roo.each(this.selectorEl.dom.files, function(file){
31562             if(this.fireEvent('inspect', this, file) != false){
31563                 this.files.push(file);
31564             }
31565         }, this);
31566         
31567         this.queue();
31568         
31569     },
31570     
31571     queue : function()
31572     {
31573         this.selectorEl.dom.value = '';
31574         
31575         if(!this.files || !this.files.length){
31576             return;
31577         }
31578         
31579         if(this.boxes > 0 && this.files.length > this.boxes){
31580             this.files = this.files.slice(0, this.boxes);
31581         }
31582         
31583         this.uploader.show();
31584         
31585         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31586             this.uploader.hide();
31587         }
31588         
31589         var _this = this;
31590         
31591         var files = [];
31592         
31593         var docs = [];
31594         
31595         Roo.each(this.files, function(file){
31596             
31597             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31598                 var f = this.renderPreview(file);
31599                 files.push(f);
31600                 return;
31601             }
31602             
31603             if(file.type.indexOf('image') != -1){
31604                 this.delegates.push(
31605                     (function(){
31606                         _this.process(file);
31607                     }).createDelegate(this)
31608                 );
31609         
31610                 return;
31611             }
31612             
31613             docs.push(
31614                 (function(){
31615                     _this.process(file);
31616                 }).createDelegate(this)
31617             );
31618             
31619         }, this);
31620         
31621         this.files = files;
31622         
31623         this.delegates = this.delegates.concat(docs);
31624         
31625         if(!this.delegates.length){
31626             this.refresh();
31627             return;
31628         }
31629         
31630         this.progressBar.aria_valuemax = this.delegates.length;
31631         
31632         this.arrange();
31633         
31634         return;
31635     },
31636     
31637     arrange : function()
31638     {
31639         if(!this.delegates.length){
31640             this.progressDialog.hide();
31641             this.refresh();
31642             return;
31643         }
31644         
31645         var delegate = this.delegates.shift();
31646         
31647         this.progressDialog.show();
31648         
31649         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31650         
31651         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31652         
31653         delegate();
31654     },
31655     
31656     refresh : function()
31657     {
31658         this.uploader.show();
31659         
31660         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31661             this.uploader.hide();
31662         }
31663         
31664         Roo.isTouch ? this.closable(false) : this.closable(true);
31665         
31666         this.fireEvent('refresh', this);
31667     },
31668     
31669     onRemove : function(e, el, o)
31670     {
31671         e.preventDefault();
31672         
31673         this.fireEvent('remove', this, o);
31674         
31675     },
31676     
31677     remove : function(o)
31678     {
31679         var files = [];
31680         
31681         Roo.each(this.files, function(file){
31682             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31683                 files.push(file);
31684                 return;
31685             }
31686
31687             o.target.remove();
31688
31689         }, this);
31690         
31691         this.files = files;
31692         
31693         this.refresh();
31694     },
31695     
31696     clear : function()
31697     {
31698         Roo.each(this.files, function(file){
31699             if(!file.target){
31700                 return;
31701             }
31702             
31703             file.target.remove();
31704
31705         }, this);
31706         
31707         this.files = [];
31708         
31709         this.refresh();
31710     },
31711     
31712     onClick : function(e, el, o)
31713     {
31714         e.preventDefault();
31715         
31716         this.fireEvent('click', this, o);
31717         
31718     },
31719     
31720     closable : function(closable)
31721     {
31722         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31723             
31724             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31725             
31726             if(closable){
31727                 el.show();
31728                 return;
31729             }
31730             
31731             el.hide();
31732             
31733         }, this);
31734     },
31735     
31736     xhrOnLoad : function(xhr)
31737     {
31738         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31739             el.remove();
31740         }, this);
31741         
31742         if (xhr.readyState !== 4) {
31743             this.arrange();
31744             this.fireEvent('exception', this, xhr);
31745             return;
31746         }
31747
31748         var response = Roo.decode(xhr.responseText);
31749         
31750         if(!response.success){
31751             this.arrange();
31752             this.fireEvent('exception', this, xhr);
31753             return;
31754         }
31755         
31756         var file = this.renderPreview(response.data);
31757         
31758         this.files.push(file);
31759         
31760         this.arrange();
31761         
31762         this.fireEvent('afterupload', this, xhr);
31763         
31764     },
31765     
31766     xhrOnError : function(xhr)
31767     {
31768         Roo.log('xhr on error');
31769         
31770         var response = Roo.decode(xhr.responseText);
31771           
31772         Roo.log(response);
31773         
31774         this.arrange();
31775     },
31776     
31777     process : function(file)
31778     {
31779         if(this.fireEvent('process', this, file) !== false){
31780             if(this.editable && file.type.indexOf('image') != -1){
31781                 this.fireEvent('edit', this, file);
31782                 return;
31783             }
31784
31785             this.uploadStart(file, false);
31786
31787             return;
31788         }
31789         
31790     },
31791     
31792     uploadStart : function(file, crop)
31793     {
31794         this.xhr = new XMLHttpRequest();
31795         
31796         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31797             this.arrange();
31798             return;
31799         }
31800         
31801         file.xhr = this.xhr;
31802             
31803         this.managerEl.createChild({
31804             tag : 'div',
31805             cls : 'roo-document-manager-loading',
31806             cn : [
31807                 {
31808                     tag : 'div',
31809                     tooltip : file.name,
31810                     cls : 'roo-document-manager-thumb',
31811                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31812                 }
31813             ]
31814
31815         });
31816
31817         this.xhr.open(this.method, this.url, true);
31818         
31819         var headers = {
31820             "Accept": "application/json",
31821             "Cache-Control": "no-cache",
31822             "X-Requested-With": "XMLHttpRequest"
31823         };
31824         
31825         for (var headerName in headers) {
31826             var headerValue = headers[headerName];
31827             if (headerValue) {
31828                 this.xhr.setRequestHeader(headerName, headerValue);
31829             }
31830         }
31831         
31832         var _this = this;
31833         
31834         this.xhr.onload = function()
31835         {
31836             _this.xhrOnLoad(_this.xhr);
31837         }
31838         
31839         this.xhr.onerror = function()
31840         {
31841             _this.xhrOnError(_this.xhr);
31842         }
31843         
31844         var formData = new FormData();
31845
31846         formData.append('returnHTML', 'NO');
31847         
31848         if(crop){
31849             formData.append('crop', crop);
31850         }
31851         
31852         formData.append(this.paramName, file, file.name);
31853         
31854         var options = {
31855             file : file, 
31856             manually : false
31857         };
31858         
31859         if(this.fireEvent('prepare', this, formData, options) != false){
31860             
31861             if(options.manually){
31862                 return;
31863             }
31864             
31865             this.xhr.send(formData);
31866             return;
31867         };
31868         
31869         this.uploadCancel();
31870     },
31871     
31872     uploadCancel : function()
31873     {
31874         if (this.xhr) {
31875             this.xhr.abort();
31876         }
31877         
31878         this.delegates = [];
31879         
31880         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31881             el.remove();
31882         }, this);
31883         
31884         this.arrange();
31885     },
31886     
31887     renderPreview : function(file)
31888     {
31889         if(typeof(file.target) != 'undefined' && file.target){
31890             return file;
31891         }
31892         
31893         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31894         
31895         var previewEl = this.managerEl.createChild({
31896             tag : 'div',
31897             cls : 'roo-document-manager-preview',
31898             cn : [
31899                 {
31900                     tag : 'div',
31901                     tooltip : file[this.toolTipName],
31902                     cls : 'roo-document-manager-thumb',
31903                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31904                 },
31905                 {
31906                     tag : 'button',
31907                     cls : 'close',
31908                     html : '<i class="fa fa-times-circle"></i>'
31909                 }
31910             ]
31911         });
31912
31913         var close = previewEl.select('button.close', true).first();
31914
31915         close.on('click', this.onRemove, this, file);
31916
31917         file.target = previewEl;
31918
31919         var image = previewEl.select('img', true).first();
31920         
31921         var _this = this;
31922         
31923         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31924         
31925         image.on('click', this.onClick, this, file);
31926         
31927         this.fireEvent('previewrendered', this, file);
31928         
31929         return file;
31930         
31931     },
31932     
31933     onPreviewLoad : function(file, image)
31934     {
31935         if(typeof(file.target) == 'undefined' || !file.target){
31936             return;
31937         }
31938         
31939         var width = image.dom.naturalWidth || image.dom.width;
31940         var height = image.dom.naturalHeight || image.dom.height;
31941         
31942         if(!this.previewResize) {
31943             return;
31944         }
31945         
31946         if(width > height){
31947             file.target.addClass('wide');
31948             return;
31949         }
31950         
31951         file.target.addClass('tall');
31952         return;
31953         
31954     },
31955     
31956     uploadFromSource : function(file, crop)
31957     {
31958         this.xhr = new XMLHttpRequest();
31959         
31960         this.managerEl.createChild({
31961             tag : 'div',
31962             cls : 'roo-document-manager-loading',
31963             cn : [
31964                 {
31965                     tag : 'div',
31966                     tooltip : file.name,
31967                     cls : 'roo-document-manager-thumb',
31968                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31969                 }
31970             ]
31971
31972         });
31973
31974         this.xhr.open(this.method, this.url, true);
31975         
31976         var headers = {
31977             "Accept": "application/json",
31978             "Cache-Control": "no-cache",
31979             "X-Requested-With": "XMLHttpRequest"
31980         };
31981         
31982         for (var headerName in headers) {
31983             var headerValue = headers[headerName];
31984             if (headerValue) {
31985                 this.xhr.setRequestHeader(headerName, headerValue);
31986             }
31987         }
31988         
31989         var _this = this;
31990         
31991         this.xhr.onload = function()
31992         {
31993             _this.xhrOnLoad(_this.xhr);
31994         }
31995         
31996         this.xhr.onerror = function()
31997         {
31998             _this.xhrOnError(_this.xhr);
31999         }
32000         
32001         var formData = new FormData();
32002
32003         formData.append('returnHTML', 'NO');
32004         
32005         formData.append('crop', crop);
32006         
32007         if(typeof(file.filename) != 'undefined'){
32008             formData.append('filename', file.filename);
32009         }
32010         
32011         if(typeof(file.mimetype) != 'undefined'){
32012             formData.append('mimetype', file.mimetype);
32013         }
32014         
32015         Roo.log(formData);
32016         
32017         if(this.fireEvent('prepare', this, formData) != false){
32018             this.xhr.send(formData);
32019         };
32020     }
32021 });
32022
32023 /*
32024 * Licence: LGPL
32025 */
32026
32027 /**
32028  * @class Roo.bootstrap.DocumentViewer
32029  * @extends Roo.bootstrap.Component
32030  * Bootstrap DocumentViewer class
32031  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32032  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32033  * 
32034  * @constructor
32035  * Create a new DocumentViewer
32036  * @param {Object} config The config object
32037  */
32038
32039 Roo.bootstrap.DocumentViewer = function(config){
32040     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32041     
32042     this.addEvents({
32043         /**
32044          * @event initial
32045          * Fire after initEvent
32046          * @param {Roo.bootstrap.DocumentViewer} this
32047          */
32048         "initial" : true,
32049         /**
32050          * @event click
32051          * Fire after click
32052          * @param {Roo.bootstrap.DocumentViewer} this
32053          */
32054         "click" : true,
32055         /**
32056          * @event download
32057          * Fire after download button
32058          * @param {Roo.bootstrap.DocumentViewer} this
32059          */
32060         "download" : true,
32061         /**
32062          * @event trash
32063          * Fire after trash button
32064          * @param {Roo.bootstrap.DocumentViewer} this
32065          */
32066         "trash" : true
32067         
32068     });
32069 };
32070
32071 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32072     
32073     showDownload : true,
32074     
32075     showTrash : true,
32076     
32077     getAutoCreate : function()
32078     {
32079         var cfg = {
32080             tag : 'div',
32081             cls : 'roo-document-viewer',
32082             cn : [
32083                 {
32084                     tag : 'div',
32085                     cls : 'roo-document-viewer-body',
32086                     cn : [
32087                         {
32088                             tag : 'div',
32089                             cls : 'roo-document-viewer-thumb',
32090                             cn : [
32091                                 {
32092                                     tag : 'img',
32093                                     cls : 'roo-document-viewer-image'
32094                                 }
32095                             ]
32096                         }
32097                     ]
32098                 },
32099                 {
32100                     tag : 'div',
32101                     cls : 'roo-document-viewer-footer',
32102                     cn : {
32103                         tag : 'div',
32104                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32105                         cn : [
32106                             {
32107                                 tag : 'div',
32108                                 cls : 'btn-group roo-document-viewer-download',
32109                                 cn : [
32110                                     {
32111                                         tag : 'button',
32112                                         cls : 'btn btn-default',
32113                                         html : '<i class="fa fa-download"></i>'
32114                                     }
32115                                 ]
32116                             },
32117                             {
32118                                 tag : 'div',
32119                                 cls : 'btn-group roo-document-viewer-trash',
32120                                 cn : [
32121                                     {
32122                                         tag : 'button',
32123                                         cls : 'btn btn-default',
32124                                         html : '<i class="fa fa-trash"></i>'
32125                                     }
32126                                 ]
32127                             }
32128                         ]
32129                     }
32130                 }
32131             ]
32132         };
32133         
32134         return cfg;
32135     },
32136     
32137     initEvents : function()
32138     {
32139         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32140         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32141         
32142         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32143         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32144         
32145         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32146         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32147         
32148         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32149         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32150         
32151         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32152         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32153         
32154         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32155         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32156         
32157         this.bodyEl.on('click', this.onClick, this);
32158         this.downloadBtn.on('click', this.onDownload, this);
32159         this.trashBtn.on('click', this.onTrash, this);
32160         
32161         this.downloadBtn.hide();
32162         this.trashBtn.hide();
32163         
32164         if(this.showDownload){
32165             this.downloadBtn.show();
32166         }
32167         
32168         if(this.showTrash){
32169             this.trashBtn.show();
32170         }
32171         
32172         if(!this.showDownload && !this.showTrash) {
32173             this.footerEl.hide();
32174         }
32175         
32176     },
32177     
32178     initial : function()
32179     {
32180         this.fireEvent('initial', this);
32181         
32182     },
32183     
32184     onClick : function(e)
32185     {
32186         e.preventDefault();
32187         
32188         this.fireEvent('click', this);
32189     },
32190     
32191     onDownload : function(e)
32192     {
32193         e.preventDefault();
32194         
32195         this.fireEvent('download', this);
32196     },
32197     
32198     onTrash : function(e)
32199     {
32200         e.preventDefault();
32201         
32202         this.fireEvent('trash', this);
32203     }
32204     
32205 });
32206 /*
32207  * - LGPL
32208  *
32209  * nav progress bar
32210  * 
32211  */
32212
32213 /**
32214  * @class Roo.bootstrap.NavProgressBar
32215  * @extends Roo.bootstrap.Component
32216  * Bootstrap NavProgressBar class
32217  * 
32218  * @constructor
32219  * Create a new nav progress bar
32220  * @param {Object} config The config object
32221  */
32222
32223 Roo.bootstrap.NavProgressBar = function(config){
32224     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32225
32226     this.bullets = this.bullets || [];
32227    
32228 //    Roo.bootstrap.NavProgressBar.register(this);
32229      this.addEvents({
32230         /**
32231              * @event changed
32232              * Fires when the active item changes
32233              * @param {Roo.bootstrap.NavProgressBar} this
32234              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32235              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32236          */
32237         'changed': true
32238      });
32239     
32240 };
32241
32242 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32243     
32244     bullets : [],
32245     barItems : [],
32246     
32247     getAutoCreate : function()
32248     {
32249         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32250         
32251         cfg = {
32252             tag : 'div',
32253             cls : 'roo-navigation-bar-group',
32254             cn : [
32255                 {
32256                     tag : 'div',
32257                     cls : 'roo-navigation-top-bar'
32258                 },
32259                 {
32260                     tag : 'div',
32261                     cls : 'roo-navigation-bullets-bar',
32262                     cn : [
32263                         {
32264                             tag : 'ul',
32265                             cls : 'roo-navigation-bar'
32266                         }
32267                     ]
32268                 },
32269                 
32270                 {
32271                     tag : 'div',
32272                     cls : 'roo-navigation-bottom-bar'
32273                 }
32274             ]
32275             
32276         };
32277         
32278         return cfg;
32279         
32280     },
32281     
32282     initEvents: function() 
32283     {
32284         
32285     },
32286     
32287     onRender : function(ct, position) 
32288     {
32289         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32290         
32291         if(this.bullets.length){
32292             Roo.each(this.bullets, function(b){
32293                this.addItem(b);
32294             }, this);
32295         }
32296         
32297         this.format();
32298         
32299     },
32300     
32301     addItem : function(cfg)
32302     {
32303         var item = new Roo.bootstrap.NavProgressItem(cfg);
32304         
32305         item.parentId = this.id;
32306         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32307         
32308         if(cfg.html){
32309             var top = new Roo.bootstrap.Element({
32310                 tag : 'div',
32311                 cls : 'roo-navigation-bar-text'
32312             });
32313             
32314             var bottom = new Roo.bootstrap.Element({
32315                 tag : 'div',
32316                 cls : 'roo-navigation-bar-text'
32317             });
32318             
32319             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32320             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32321             
32322             var topText = new Roo.bootstrap.Element({
32323                 tag : 'span',
32324                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32325             });
32326             
32327             var bottomText = new Roo.bootstrap.Element({
32328                 tag : 'span',
32329                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32330             });
32331             
32332             topText.onRender(top.el, null);
32333             bottomText.onRender(bottom.el, null);
32334             
32335             item.topEl = top;
32336             item.bottomEl = bottom;
32337         }
32338         
32339         this.barItems.push(item);
32340         
32341         return item;
32342     },
32343     
32344     getActive : function()
32345     {
32346         var active = false;
32347         
32348         Roo.each(this.barItems, function(v){
32349             
32350             if (!v.isActive()) {
32351                 return;
32352             }
32353             
32354             active = v;
32355             return false;
32356             
32357         });
32358         
32359         return active;
32360     },
32361     
32362     setActiveItem : function(item)
32363     {
32364         var prev = false;
32365         
32366         Roo.each(this.barItems, function(v){
32367             if (v.rid == item.rid) {
32368                 return ;
32369             }
32370             
32371             if (v.isActive()) {
32372                 v.setActive(false);
32373                 prev = v;
32374             }
32375         });
32376
32377         item.setActive(true);
32378         
32379         this.fireEvent('changed', this, item, prev);
32380     },
32381     
32382     getBarItem: function(rid)
32383     {
32384         var ret = false;
32385         
32386         Roo.each(this.barItems, function(e) {
32387             if (e.rid != rid) {
32388                 return;
32389             }
32390             
32391             ret =  e;
32392             return false;
32393         });
32394         
32395         return ret;
32396     },
32397     
32398     indexOfItem : function(item)
32399     {
32400         var index = false;
32401         
32402         Roo.each(this.barItems, function(v, i){
32403             
32404             if (v.rid != item.rid) {
32405                 return;
32406             }
32407             
32408             index = i;
32409             return false
32410         });
32411         
32412         return index;
32413     },
32414     
32415     setActiveNext : function()
32416     {
32417         var i = this.indexOfItem(this.getActive());
32418         
32419         if (i > this.barItems.length) {
32420             return;
32421         }
32422         
32423         this.setActiveItem(this.barItems[i+1]);
32424     },
32425     
32426     setActivePrev : function()
32427     {
32428         var i = this.indexOfItem(this.getActive());
32429         
32430         if (i  < 1) {
32431             return;
32432         }
32433         
32434         this.setActiveItem(this.barItems[i-1]);
32435     },
32436     
32437     format : function()
32438     {
32439         if(!this.barItems.length){
32440             return;
32441         }
32442      
32443         var width = 100 / this.barItems.length;
32444         
32445         Roo.each(this.barItems, function(i){
32446             i.el.setStyle('width', width + '%');
32447             i.topEl.el.setStyle('width', width + '%');
32448             i.bottomEl.el.setStyle('width', width + '%');
32449         }, this);
32450         
32451     }
32452     
32453 });
32454 /*
32455  * - LGPL
32456  *
32457  * Nav Progress Item
32458  * 
32459  */
32460
32461 /**
32462  * @class Roo.bootstrap.NavProgressItem
32463  * @extends Roo.bootstrap.Component
32464  * Bootstrap NavProgressItem class
32465  * @cfg {String} rid the reference id
32466  * @cfg {Boolean} active (true|false) Is item active default false
32467  * @cfg {Boolean} disabled (true|false) Is item active default false
32468  * @cfg {String} html
32469  * @cfg {String} position (top|bottom) text position default bottom
32470  * @cfg {String} icon show icon instead of number
32471  * 
32472  * @constructor
32473  * Create a new NavProgressItem
32474  * @param {Object} config The config object
32475  */
32476 Roo.bootstrap.NavProgressItem = function(config){
32477     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32478     this.addEvents({
32479         // raw events
32480         /**
32481          * @event click
32482          * The raw click event for the entire grid.
32483          * @param {Roo.bootstrap.NavProgressItem} this
32484          * @param {Roo.EventObject} e
32485          */
32486         "click" : true
32487     });
32488    
32489 };
32490
32491 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32492     
32493     rid : '',
32494     active : false,
32495     disabled : false,
32496     html : '',
32497     position : 'bottom',
32498     icon : false,
32499     
32500     getAutoCreate : function()
32501     {
32502         var iconCls = 'roo-navigation-bar-item-icon';
32503         
32504         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32505         
32506         var cfg = {
32507             tag: 'li',
32508             cls: 'roo-navigation-bar-item',
32509             cn : [
32510                 {
32511                     tag : 'i',
32512                     cls : iconCls
32513                 }
32514             ]
32515         };
32516         
32517         if(this.active){
32518             cfg.cls += ' active';
32519         }
32520         if(this.disabled){
32521             cfg.cls += ' disabled';
32522         }
32523         
32524         return cfg;
32525     },
32526     
32527     disable : function()
32528     {
32529         this.setDisabled(true);
32530     },
32531     
32532     enable : function()
32533     {
32534         this.setDisabled(false);
32535     },
32536     
32537     initEvents: function() 
32538     {
32539         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32540         
32541         this.iconEl.on('click', this.onClick, this);
32542     },
32543     
32544     onClick : function(e)
32545     {
32546         e.preventDefault();
32547         
32548         if(this.disabled){
32549             return;
32550         }
32551         
32552         if(this.fireEvent('click', this, e) === false){
32553             return;
32554         };
32555         
32556         this.parent().setActiveItem(this);
32557     },
32558     
32559     isActive: function () 
32560     {
32561         return this.active;
32562     },
32563     
32564     setActive : function(state)
32565     {
32566         if(this.active == state){
32567             return;
32568         }
32569         
32570         this.active = state;
32571         
32572         if (state) {
32573             this.el.addClass('active');
32574             return;
32575         }
32576         
32577         this.el.removeClass('active');
32578         
32579         return;
32580     },
32581     
32582     setDisabled : function(state)
32583     {
32584         if(this.disabled == state){
32585             return;
32586         }
32587         
32588         this.disabled = state;
32589         
32590         if (state) {
32591             this.el.addClass('disabled');
32592             return;
32593         }
32594         
32595         this.el.removeClass('disabled');
32596     },
32597     
32598     tooltipEl : function()
32599     {
32600         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32601     }
32602 });
32603  
32604
32605  /*
32606  * - LGPL
32607  *
32608  * FieldLabel
32609  * 
32610  */
32611
32612 /**
32613  * @class Roo.bootstrap.FieldLabel
32614  * @extends Roo.bootstrap.Component
32615  * Bootstrap FieldLabel class
32616  * @cfg {String} html contents of the element
32617  * @cfg {String} tag tag of the element default label
32618  * @cfg {String} cls class of the element
32619  * @cfg {String} target label target 
32620  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32621  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32622  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32623  * @cfg {String} iconTooltip default "This field is required"
32624  * @cfg {String} indicatorpos (left|right) default left
32625  * 
32626  * @constructor
32627  * Create a new FieldLabel
32628  * @param {Object} config The config object
32629  */
32630
32631 Roo.bootstrap.FieldLabel = function(config){
32632     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32633     
32634     this.addEvents({
32635             /**
32636              * @event invalid
32637              * Fires after the field has been marked as invalid.
32638              * @param {Roo.form.FieldLabel} this
32639              * @param {String} msg The validation message
32640              */
32641             invalid : true,
32642             /**
32643              * @event valid
32644              * Fires after the field has been validated with no errors.
32645              * @param {Roo.form.FieldLabel} this
32646              */
32647             valid : true
32648         });
32649 };
32650
32651 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32652     
32653     tag: 'label',
32654     cls: '',
32655     html: '',
32656     target: '',
32657     allowBlank : true,
32658     invalidClass : 'has-warning',
32659     validClass : 'has-success',
32660     iconTooltip : 'This field is required',
32661     indicatorpos : 'left',
32662     
32663     getAutoCreate : function(){
32664         
32665         var cls = "";
32666         if (!this.allowBlank) {
32667             cls  = "visible";
32668         }
32669         
32670         var cfg = {
32671             tag : this.tag,
32672             cls : 'roo-bootstrap-field-label ' + this.cls,
32673             for : this.target,
32674             cn : [
32675                 {
32676                     tag : 'i',
32677                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32678                     tooltip : this.iconTooltip
32679                 },
32680                 {
32681                     tag : 'span',
32682                     html : this.html
32683                 }
32684             ] 
32685         };
32686         
32687         if(this.indicatorpos == 'right'){
32688             var cfg = {
32689                 tag : this.tag,
32690                 cls : 'roo-bootstrap-field-label ' + this.cls,
32691                 for : this.target,
32692                 cn : [
32693                     {
32694                         tag : 'span',
32695                         html : this.html
32696                     },
32697                     {
32698                         tag : 'i',
32699                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32700                         tooltip : this.iconTooltip
32701                     }
32702                 ] 
32703             };
32704         }
32705         
32706         return cfg;
32707     },
32708     
32709     initEvents: function() 
32710     {
32711         Roo.bootstrap.Element.superclass.initEvents.call(this);
32712         
32713         this.indicator = this.indicatorEl();
32714         
32715         if(this.indicator){
32716             this.indicator.removeClass('visible');
32717             this.indicator.addClass('invisible');
32718         }
32719         
32720         Roo.bootstrap.FieldLabel.register(this);
32721     },
32722     
32723     indicatorEl : function()
32724     {
32725         var indicator = this.el.select('i.roo-required-indicator',true).first();
32726         
32727         if(!indicator){
32728             return false;
32729         }
32730         
32731         return indicator;
32732         
32733     },
32734     
32735     /**
32736      * Mark this field as valid
32737      */
32738     markValid : function()
32739     {
32740         if(this.indicator){
32741             this.indicator.removeClass('visible');
32742             this.indicator.addClass('invisible');
32743         }
32744         if (Roo.bootstrap.version == 3) {
32745             this.el.removeClass(this.invalidClass);
32746             this.el.addClass(this.validClass);
32747         } else {
32748             this.el.removeClass('is-invalid');
32749             this.el.addClass('is-valid');
32750         }
32751         
32752         
32753         this.fireEvent('valid', this);
32754     },
32755     
32756     /**
32757      * Mark this field as invalid
32758      * @param {String} msg The validation message
32759      */
32760     markInvalid : function(msg)
32761     {
32762         if(this.indicator){
32763             this.indicator.removeClass('invisible');
32764             this.indicator.addClass('visible');
32765         }
32766           if (Roo.bootstrap.version == 3) {
32767             this.el.removeClass(this.validClass);
32768             this.el.addClass(this.invalidClass);
32769         } else {
32770             this.el.removeClass('is-valid');
32771             this.el.addClass('is-invalid');
32772         }
32773         
32774         
32775         this.fireEvent('invalid', this, msg);
32776     }
32777     
32778    
32779 });
32780
32781 Roo.apply(Roo.bootstrap.FieldLabel, {
32782     
32783     groups: {},
32784     
32785      /**
32786     * register a FieldLabel Group
32787     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32788     */
32789     register : function(label)
32790     {
32791         if(this.groups.hasOwnProperty(label.target)){
32792             return;
32793         }
32794      
32795         this.groups[label.target] = label;
32796         
32797     },
32798     /**
32799     * fetch a FieldLabel Group based on the target
32800     * @param {string} target
32801     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32802     */
32803     get: function(target) {
32804         if (typeof(this.groups[target]) == 'undefined') {
32805             return false;
32806         }
32807         
32808         return this.groups[target] ;
32809     }
32810 });
32811
32812  
32813
32814  /*
32815  * - LGPL
32816  *
32817  * page DateSplitField.
32818  * 
32819  */
32820
32821
32822 /**
32823  * @class Roo.bootstrap.DateSplitField
32824  * @extends Roo.bootstrap.Component
32825  * Bootstrap DateSplitField class
32826  * @cfg {string} fieldLabel - the label associated
32827  * @cfg {Number} labelWidth set the width of label (0-12)
32828  * @cfg {String} labelAlign (top|left)
32829  * @cfg {Boolean} dayAllowBlank (true|false) default false
32830  * @cfg {Boolean} monthAllowBlank (true|false) default false
32831  * @cfg {Boolean} yearAllowBlank (true|false) default false
32832  * @cfg {string} dayPlaceholder 
32833  * @cfg {string} monthPlaceholder
32834  * @cfg {string} yearPlaceholder
32835  * @cfg {string} dayFormat default 'd'
32836  * @cfg {string} monthFormat default 'm'
32837  * @cfg {string} yearFormat default 'Y'
32838  * @cfg {Number} labellg set the width of label (1-12)
32839  * @cfg {Number} labelmd set the width of label (1-12)
32840  * @cfg {Number} labelsm set the width of label (1-12)
32841  * @cfg {Number} labelxs set the width of label (1-12)
32842
32843  *     
32844  * @constructor
32845  * Create a new DateSplitField
32846  * @param {Object} config The config object
32847  */
32848
32849 Roo.bootstrap.DateSplitField = function(config){
32850     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32851     
32852     this.addEvents({
32853         // raw events
32854          /**
32855          * @event years
32856          * getting the data of years
32857          * @param {Roo.bootstrap.DateSplitField} this
32858          * @param {Object} years
32859          */
32860         "years" : true,
32861         /**
32862          * @event days
32863          * getting the data of days
32864          * @param {Roo.bootstrap.DateSplitField} this
32865          * @param {Object} days
32866          */
32867         "days" : true,
32868         /**
32869          * @event invalid
32870          * Fires after the field has been marked as invalid.
32871          * @param {Roo.form.Field} this
32872          * @param {String} msg The validation message
32873          */
32874         invalid : true,
32875        /**
32876          * @event valid
32877          * Fires after the field has been validated with no errors.
32878          * @param {Roo.form.Field} this
32879          */
32880         valid : true
32881     });
32882 };
32883
32884 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32885     
32886     fieldLabel : '',
32887     labelAlign : 'top',
32888     labelWidth : 3,
32889     dayAllowBlank : false,
32890     monthAllowBlank : false,
32891     yearAllowBlank : false,
32892     dayPlaceholder : '',
32893     monthPlaceholder : '',
32894     yearPlaceholder : '',
32895     dayFormat : 'd',
32896     monthFormat : 'm',
32897     yearFormat : 'Y',
32898     isFormField : true,
32899     labellg : 0,
32900     labelmd : 0,
32901     labelsm : 0,
32902     labelxs : 0,
32903     
32904     getAutoCreate : function()
32905     {
32906         var cfg = {
32907             tag : 'div',
32908             cls : 'row roo-date-split-field-group',
32909             cn : [
32910                 {
32911                     tag : 'input',
32912                     type : 'hidden',
32913                     cls : 'form-hidden-field roo-date-split-field-group-value',
32914                     name : this.name
32915                 }
32916             ]
32917         };
32918         
32919         var labelCls = 'col-md-12';
32920         var contentCls = 'col-md-4';
32921         
32922         if(this.fieldLabel){
32923             
32924             var label = {
32925                 tag : 'div',
32926                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32927                 cn : [
32928                     {
32929                         tag : 'label',
32930                         html : this.fieldLabel
32931                     }
32932                 ]
32933             };
32934             
32935             if(this.labelAlign == 'left'){
32936             
32937                 if(this.labelWidth > 12){
32938                     label.style = "width: " + this.labelWidth + 'px';
32939                 }
32940
32941                 if(this.labelWidth < 13 && this.labelmd == 0){
32942                     this.labelmd = this.labelWidth;
32943                 }
32944
32945                 if(this.labellg > 0){
32946                     labelCls = ' col-lg-' + this.labellg;
32947                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32948                 }
32949
32950                 if(this.labelmd > 0){
32951                     labelCls = ' col-md-' + this.labelmd;
32952                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32953                 }
32954
32955                 if(this.labelsm > 0){
32956                     labelCls = ' col-sm-' + this.labelsm;
32957                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32958                 }
32959
32960                 if(this.labelxs > 0){
32961                     labelCls = ' col-xs-' + this.labelxs;
32962                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32963                 }
32964             }
32965             
32966             label.cls += ' ' + labelCls;
32967             
32968             cfg.cn.push(label);
32969         }
32970         
32971         Roo.each(['day', 'month', 'year'], function(t){
32972             cfg.cn.push({
32973                 tag : 'div',
32974                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32975             });
32976         }, this);
32977         
32978         return cfg;
32979     },
32980     
32981     inputEl: function ()
32982     {
32983         return this.el.select('.roo-date-split-field-group-value', true).first();
32984     },
32985     
32986     onRender : function(ct, position) 
32987     {
32988         var _this = this;
32989         
32990         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32991         
32992         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32993         
32994         this.dayField = new Roo.bootstrap.ComboBox({
32995             allowBlank : this.dayAllowBlank,
32996             alwaysQuery : true,
32997             displayField : 'value',
32998             editable : false,
32999             fieldLabel : '',
33000             forceSelection : true,
33001             mode : 'local',
33002             placeholder : this.dayPlaceholder,
33003             selectOnFocus : true,
33004             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33005             triggerAction : 'all',
33006             typeAhead : true,
33007             valueField : 'value',
33008             store : new Roo.data.SimpleStore({
33009                 data : (function() {    
33010                     var days = [];
33011                     _this.fireEvent('days', _this, days);
33012                     return days;
33013                 })(),
33014                 fields : [ 'value' ]
33015             }),
33016             listeners : {
33017                 select : function (_self, record, index)
33018                 {
33019                     _this.setValue(_this.getValue());
33020                 }
33021             }
33022         });
33023
33024         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33025         
33026         this.monthField = new Roo.bootstrap.MonthField({
33027             after : '<i class=\"fa fa-calendar\"></i>',
33028             allowBlank : this.monthAllowBlank,
33029             placeholder : this.monthPlaceholder,
33030             readOnly : true,
33031             listeners : {
33032                 render : function (_self)
33033                 {
33034                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33035                         e.preventDefault();
33036                         _self.focus();
33037                     });
33038                 },
33039                 select : function (_self, oldvalue, newvalue)
33040                 {
33041                     _this.setValue(_this.getValue());
33042                 }
33043             }
33044         });
33045         
33046         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33047         
33048         this.yearField = new Roo.bootstrap.ComboBox({
33049             allowBlank : this.yearAllowBlank,
33050             alwaysQuery : true,
33051             displayField : 'value',
33052             editable : false,
33053             fieldLabel : '',
33054             forceSelection : true,
33055             mode : 'local',
33056             placeholder : this.yearPlaceholder,
33057             selectOnFocus : true,
33058             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33059             triggerAction : 'all',
33060             typeAhead : true,
33061             valueField : 'value',
33062             store : new Roo.data.SimpleStore({
33063                 data : (function() {
33064                     var years = [];
33065                     _this.fireEvent('years', _this, years);
33066                     return years;
33067                 })(),
33068                 fields : [ 'value' ]
33069             }),
33070             listeners : {
33071                 select : function (_self, record, index)
33072                 {
33073                     _this.setValue(_this.getValue());
33074                 }
33075             }
33076         });
33077
33078         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33079     },
33080     
33081     setValue : function(v, format)
33082     {
33083         this.inputEl.dom.value = v;
33084         
33085         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33086         
33087         var d = Date.parseDate(v, f);
33088         
33089         if(!d){
33090             this.validate();
33091             return;
33092         }
33093         
33094         this.setDay(d.format(this.dayFormat));
33095         this.setMonth(d.format(this.monthFormat));
33096         this.setYear(d.format(this.yearFormat));
33097         
33098         this.validate();
33099         
33100         return;
33101     },
33102     
33103     setDay : function(v)
33104     {
33105         this.dayField.setValue(v);
33106         this.inputEl.dom.value = this.getValue();
33107         this.validate();
33108         return;
33109     },
33110     
33111     setMonth : function(v)
33112     {
33113         this.monthField.setValue(v, true);
33114         this.inputEl.dom.value = this.getValue();
33115         this.validate();
33116         return;
33117     },
33118     
33119     setYear : function(v)
33120     {
33121         this.yearField.setValue(v);
33122         this.inputEl.dom.value = this.getValue();
33123         this.validate();
33124         return;
33125     },
33126     
33127     getDay : function()
33128     {
33129         return this.dayField.getValue();
33130     },
33131     
33132     getMonth : function()
33133     {
33134         return this.monthField.getValue();
33135     },
33136     
33137     getYear : function()
33138     {
33139         return this.yearField.getValue();
33140     },
33141     
33142     getValue : function()
33143     {
33144         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33145         
33146         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33147         
33148         return date;
33149     },
33150     
33151     reset : function()
33152     {
33153         this.setDay('');
33154         this.setMonth('');
33155         this.setYear('');
33156         this.inputEl.dom.value = '';
33157         this.validate();
33158         return;
33159     },
33160     
33161     validate : function()
33162     {
33163         var d = this.dayField.validate();
33164         var m = this.monthField.validate();
33165         var y = this.yearField.validate();
33166         
33167         var valid = true;
33168         
33169         if(
33170                 (!this.dayAllowBlank && !d) ||
33171                 (!this.monthAllowBlank && !m) ||
33172                 (!this.yearAllowBlank && !y)
33173         ){
33174             valid = false;
33175         }
33176         
33177         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33178             return valid;
33179         }
33180         
33181         if(valid){
33182             this.markValid();
33183             return valid;
33184         }
33185         
33186         this.markInvalid();
33187         
33188         return valid;
33189     },
33190     
33191     markValid : function()
33192     {
33193         
33194         var label = this.el.select('label', true).first();
33195         var icon = this.el.select('i.fa-star', true).first();
33196
33197         if(label && icon){
33198             icon.remove();
33199         }
33200         
33201         this.fireEvent('valid', this);
33202     },
33203     
33204      /**
33205      * Mark this field as invalid
33206      * @param {String} msg The validation message
33207      */
33208     markInvalid : function(msg)
33209     {
33210         
33211         var label = this.el.select('label', true).first();
33212         var icon = this.el.select('i.fa-star', true).first();
33213
33214         if(label && !icon){
33215             this.el.select('.roo-date-split-field-label', true).createChild({
33216                 tag : 'i',
33217                 cls : 'text-danger fa fa-lg fa-star',
33218                 tooltip : 'This field is required',
33219                 style : 'margin-right:5px;'
33220             }, label, true);
33221         }
33222         
33223         this.fireEvent('invalid', this, msg);
33224     },
33225     
33226     clearInvalid : function()
33227     {
33228         var label = this.el.select('label', true).first();
33229         var icon = this.el.select('i.fa-star', true).first();
33230
33231         if(label && icon){
33232             icon.remove();
33233         }
33234         
33235         this.fireEvent('valid', this);
33236     },
33237     
33238     getName: function()
33239     {
33240         return this.name;
33241     }
33242     
33243 });
33244
33245  /**
33246  *
33247  * This is based on 
33248  * http://masonry.desandro.com
33249  *
33250  * The idea is to render all the bricks based on vertical width...
33251  *
33252  * The original code extends 'outlayer' - we might need to use that....
33253  * 
33254  */
33255
33256
33257 /**
33258  * @class Roo.bootstrap.LayoutMasonry
33259  * @extends Roo.bootstrap.Component
33260  * Bootstrap Layout Masonry class
33261  * 
33262  * @constructor
33263  * Create a new Element
33264  * @param {Object} config The config object
33265  */
33266
33267 Roo.bootstrap.LayoutMasonry = function(config){
33268     
33269     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33270     
33271     this.bricks = [];
33272     
33273     Roo.bootstrap.LayoutMasonry.register(this);
33274     
33275     this.addEvents({
33276         // raw events
33277         /**
33278          * @event layout
33279          * Fire after layout the items
33280          * @param {Roo.bootstrap.LayoutMasonry} this
33281          * @param {Roo.EventObject} e
33282          */
33283         "layout" : true
33284     });
33285     
33286 };
33287
33288 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33289     
33290     /**
33291      * @cfg {Boolean} isLayoutInstant = no animation?
33292      */   
33293     isLayoutInstant : false, // needed?
33294    
33295     /**
33296      * @cfg {Number} boxWidth  width of the columns
33297      */   
33298     boxWidth : 450,
33299     
33300       /**
33301      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33302      */   
33303     boxHeight : 0,
33304     
33305     /**
33306      * @cfg {Number} padWidth padding below box..
33307      */   
33308     padWidth : 10, 
33309     
33310     /**
33311      * @cfg {Number} gutter gutter width..
33312      */   
33313     gutter : 10,
33314     
33315      /**
33316      * @cfg {Number} maxCols maximum number of columns
33317      */   
33318     
33319     maxCols: 0,
33320     
33321     /**
33322      * @cfg {Boolean} isAutoInitial defalut true
33323      */   
33324     isAutoInitial : true, 
33325     
33326     containerWidth: 0,
33327     
33328     /**
33329      * @cfg {Boolean} isHorizontal defalut false
33330      */   
33331     isHorizontal : false, 
33332
33333     currentSize : null,
33334     
33335     tag: 'div',
33336     
33337     cls: '',
33338     
33339     bricks: null, //CompositeElement
33340     
33341     cols : 1,
33342     
33343     _isLayoutInited : false,
33344     
33345 //    isAlternative : false, // only use for vertical layout...
33346     
33347     /**
33348      * @cfg {Number} alternativePadWidth padding below box..
33349      */   
33350     alternativePadWidth : 50,
33351     
33352     selectedBrick : [],
33353     
33354     getAutoCreate : function(){
33355         
33356         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33357         
33358         var cfg = {
33359             tag: this.tag,
33360             cls: 'blog-masonary-wrapper ' + this.cls,
33361             cn : {
33362                 cls : 'mas-boxes masonary'
33363             }
33364         };
33365         
33366         return cfg;
33367     },
33368     
33369     getChildContainer: function( )
33370     {
33371         if (this.boxesEl) {
33372             return this.boxesEl;
33373         }
33374         
33375         this.boxesEl = this.el.select('.mas-boxes').first();
33376         
33377         return this.boxesEl;
33378     },
33379     
33380     
33381     initEvents : function()
33382     {
33383         var _this = this;
33384         
33385         if(this.isAutoInitial){
33386             Roo.log('hook children rendered');
33387             this.on('childrenrendered', function() {
33388                 Roo.log('children rendered');
33389                 _this.initial();
33390             } ,this);
33391         }
33392     },
33393     
33394     initial : function()
33395     {
33396         this.selectedBrick = [];
33397         
33398         this.currentSize = this.el.getBox(true);
33399         
33400         Roo.EventManager.onWindowResize(this.resize, this); 
33401
33402         if(!this.isAutoInitial){
33403             this.layout();
33404             return;
33405         }
33406         
33407         this.layout();
33408         
33409         return;
33410         //this.layout.defer(500,this);
33411         
33412     },
33413     
33414     resize : function()
33415     {
33416         var cs = this.el.getBox(true);
33417         
33418         if (
33419                 this.currentSize.width == cs.width && 
33420                 this.currentSize.x == cs.x && 
33421                 this.currentSize.height == cs.height && 
33422                 this.currentSize.y == cs.y 
33423         ) {
33424             Roo.log("no change in with or X or Y");
33425             return;
33426         }
33427         
33428         this.currentSize = cs;
33429         
33430         this.layout();
33431         
33432     },
33433     
33434     layout : function()
33435     {   
33436         this._resetLayout();
33437         
33438         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33439         
33440         this.layoutItems( isInstant );
33441       
33442         this._isLayoutInited = true;
33443         
33444         this.fireEvent('layout', this);
33445         
33446     },
33447     
33448     _resetLayout : function()
33449     {
33450         if(this.isHorizontal){
33451             this.horizontalMeasureColumns();
33452             return;
33453         }
33454         
33455         this.verticalMeasureColumns();
33456         
33457     },
33458     
33459     verticalMeasureColumns : function()
33460     {
33461         this.getContainerWidth();
33462         
33463 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33464 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33465 //            return;
33466 //        }
33467         
33468         var boxWidth = this.boxWidth + this.padWidth;
33469         
33470         if(this.containerWidth < this.boxWidth){
33471             boxWidth = this.containerWidth
33472         }
33473         
33474         var containerWidth = this.containerWidth;
33475         
33476         var cols = Math.floor(containerWidth / boxWidth);
33477         
33478         this.cols = Math.max( cols, 1 );
33479         
33480         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33481         
33482         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33483         
33484         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33485         
33486         this.colWidth = boxWidth + avail - this.padWidth;
33487         
33488         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33489         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33490     },
33491     
33492     horizontalMeasureColumns : function()
33493     {
33494         this.getContainerWidth();
33495         
33496         var boxWidth = this.boxWidth;
33497         
33498         if(this.containerWidth < boxWidth){
33499             boxWidth = this.containerWidth;
33500         }
33501         
33502         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33503         
33504         this.el.setHeight(boxWidth);
33505         
33506     },
33507     
33508     getContainerWidth : function()
33509     {
33510         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33511     },
33512     
33513     layoutItems : function( isInstant )
33514     {
33515         Roo.log(this.bricks);
33516         
33517         var items = Roo.apply([], this.bricks);
33518         
33519         if(this.isHorizontal){
33520             this._horizontalLayoutItems( items , isInstant );
33521             return;
33522         }
33523         
33524 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33525 //            this._verticalAlternativeLayoutItems( items , isInstant );
33526 //            return;
33527 //        }
33528         
33529         this._verticalLayoutItems( items , isInstant );
33530         
33531     },
33532     
33533     _verticalLayoutItems : function ( items , isInstant)
33534     {
33535         if ( !items || !items.length ) {
33536             return;
33537         }
33538         
33539         var standard = [
33540             ['xs', 'xs', 'xs', 'tall'],
33541             ['xs', 'xs', 'tall'],
33542             ['xs', 'xs', 'sm'],
33543             ['xs', 'xs', 'xs'],
33544             ['xs', 'tall'],
33545             ['xs', 'sm'],
33546             ['xs', 'xs'],
33547             ['xs'],
33548             
33549             ['sm', 'xs', 'xs'],
33550             ['sm', 'xs'],
33551             ['sm'],
33552             
33553             ['tall', 'xs', 'xs', 'xs'],
33554             ['tall', 'xs', 'xs'],
33555             ['tall', 'xs'],
33556             ['tall']
33557             
33558         ];
33559         
33560         var queue = [];
33561         
33562         var boxes = [];
33563         
33564         var box = [];
33565         
33566         Roo.each(items, function(item, k){
33567             
33568             switch (item.size) {
33569                 // these layouts take up a full box,
33570                 case 'md' :
33571                 case 'md-left' :
33572                 case 'md-right' :
33573                 case 'wide' :
33574                     
33575                     if(box.length){
33576                         boxes.push(box);
33577                         box = [];
33578                     }
33579                     
33580                     boxes.push([item]);
33581                     
33582                     break;
33583                     
33584                 case 'xs' :
33585                 case 'sm' :
33586                 case 'tall' :
33587                     
33588                     box.push(item);
33589                     
33590                     break;
33591                 default :
33592                     break;
33593                     
33594             }
33595             
33596         }, this);
33597         
33598         if(box.length){
33599             boxes.push(box);
33600             box = [];
33601         }
33602         
33603         var filterPattern = function(box, length)
33604         {
33605             if(!box.length){
33606                 return;
33607             }
33608             
33609             var match = false;
33610             
33611             var pattern = box.slice(0, length);
33612             
33613             var format = [];
33614             
33615             Roo.each(pattern, function(i){
33616                 format.push(i.size);
33617             }, this);
33618             
33619             Roo.each(standard, function(s){
33620                 
33621                 if(String(s) != String(format)){
33622                     return;
33623                 }
33624                 
33625                 match = true;
33626                 return false;
33627                 
33628             }, this);
33629             
33630             if(!match && length == 1){
33631                 return;
33632             }
33633             
33634             if(!match){
33635                 filterPattern(box, length - 1);
33636                 return;
33637             }
33638                 
33639             queue.push(pattern);
33640
33641             box = box.slice(length, box.length);
33642
33643             filterPattern(box, 4);
33644
33645             return;
33646             
33647         }
33648         
33649         Roo.each(boxes, function(box, k){
33650             
33651             if(!box.length){
33652                 return;
33653             }
33654             
33655             if(box.length == 1){
33656                 queue.push(box);
33657                 return;
33658             }
33659             
33660             filterPattern(box, 4);
33661             
33662         }, this);
33663         
33664         this._processVerticalLayoutQueue( queue, isInstant );
33665         
33666     },
33667     
33668 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33669 //    {
33670 //        if ( !items || !items.length ) {
33671 //            return;
33672 //        }
33673 //
33674 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33675 //        
33676 //    },
33677     
33678     _horizontalLayoutItems : function ( items , isInstant)
33679     {
33680         if ( !items || !items.length || items.length < 3) {
33681             return;
33682         }
33683         
33684         items.reverse();
33685         
33686         var eItems = items.slice(0, 3);
33687         
33688         items = items.slice(3, items.length);
33689         
33690         var standard = [
33691             ['xs', 'xs', 'xs', 'wide'],
33692             ['xs', 'xs', 'wide'],
33693             ['xs', 'xs', 'sm'],
33694             ['xs', 'xs', 'xs'],
33695             ['xs', 'wide'],
33696             ['xs', 'sm'],
33697             ['xs', 'xs'],
33698             ['xs'],
33699             
33700             ['sm', 'xs', 'xs'],
33701             ['sm', 'xs'],
33702             ['sm'],
33703             
33704             ['wide', 'xs', 'xs', 'xs'],
33705             ['wide', 'xs', 'xs'],
33706             ['wide', 'xs'],
33707             ['wide'],
33708             
33709             ['wide-thin']
33710         ];
33711         
33712         var queue = [];
33713         
33714         var boxes = [];
33715         
33716         var box = [];
33717         
33718         Roo.each(items, function(item, k){
33719             
33720             switch (item.size) {
33721                 case 'md' :
33722                 case 'md-left' :
33723                 case 'md-right' :
33724                 case 'tall' :
33725                     
33726                     if(box.length){
33727                         boxes.push(box);
33728                         box = [];
33729                     }
33730                     
33731                     boxes.push([item]);
33732                     
33733                     break;
33734                     
33735                 case 'xs' :
33736                 case 'sm' :
33737                 case 'wide' :
33738                 case 'wide-thin' :
33739                     
33740                     box.push(item);
33741                     
33742                     break;
33743                 default :
33744                     break;
33745                     
33746             }
33747             
33748         }, this);
33749         
33750         if(box.length){
33751             boxes.push(box);
33752             box = [];
33753         }
33754         
33755         var filterPattern = function(box, length)
33756         {
33757             if(!box.length){
33758                 return;
33759             }
33760             
33761             var match = false;
33762             
33763             var pattern = box.slice(0, length);
33764             
33765             var format = [];
33766             
33767             Roo.each(pattern, function(i){
33768                 format.push(i.size);
33769             }, this);
33770             
33771             Roo.each(standard, function(s){
33772                 
33773                 if(String(s) != String(format)){
33774                     return;
33775                 }
33776                 
33777                 match = true;
33778                 return false;
33779                 
33780             }, this);
33781             
33782             if(!match && length == 1){
33783                 return;
33784             }
33785             
33786             if(!match){
33787                 filterPattern(box, length - 1);
33788                 return;
33789             }
33790                 
33791             queue.push(pattern);
33792
33793             box = box.slice(length, box.length);
33794
33795             filterPattern(box, 4);
33796
33797             return;
33798             
33799         }
33800         
33801         Roo.each(boxes, function(box, k){
33802             
33803             if(!box.length){
33804                 return;
33805             }
33806             
33807             if(box.length == 1){
33808                 queue.push(box);
33809                 return;
33810             }
33811             
33812             filterPattern(box, 4);
33813             
33814         }, this);
33815         
33816         
33817         var prune = [];
33818         
33819         var pos = this.el.getBox(true);
33820         
33821         var minX = pos.x;
33822         
33823         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33824         
33825         var hit_end = false;
33826         
33827         Roo.each(queue, function(box){
33828             
33829             if(hit_end){
33830                 
33831                 Roo.each(box, function(b){
33832                 
33833                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33834                     b.el.hide();
33835
33836                 }, this);
33837
33838                 return;
33839             }
33840             
33841             var mx = 0;
33842             
33843             Roo.each(box, function(b){
33844                 
33845                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33846                 b.el.show();
33847
33848                 mx = Math.max(mx, b.x);
33849                 
33850             }, this);
33851             
33852             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33853             
33854             if(maxX < minX){
33855                 
33856                 Roo.each(box, function(b){
33857                 
33858                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33859                     b.el.hide();
33860                     
33861                 }, this);
33862                 
33863                 hit_end = true;
33864                 
33865                 return;
33866             }
33867             
33868             prune.push(box);
33869             
33870         }, this);
33871         
33872         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33873     },
33874     
33875     /** Sets position of item in DOM
33876     * @param {Element} item
33877     * @param {Number} x - horizontal position
33878     * @param {Number} y - vertical position
33879     * @param {Boolean} isInstant - disables transitions
33880     */
33881     _processVerticalLayoutQueue : function( queue, isInstant )
33882     {
33883         var pos = this.el.getBox(true);
33884         var x = pos.x;
33885         var y = pos.y;
33886         var maxY = [];
33887         
33888         for (var i = 0; i < this.cols; i++){
33889             maxY[i] = pos.y;
33890         }
33891         
33892         Roo.each(queue, function(box, k){
33893             
33894             var col = k % this.cols;
33895             
33896             Roo.each(box, function(b,kk){
33897                 
33898                 b.el.position('absolute');
33899                 
33900                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33901                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33902                 
33903                 if(b.size == 'md-left' || b.size == 'md-right'){
33904                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33905                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33906                 }
33907                 
33908                 b.el.setWidth(width);
33909                 b.el.setHeight(height);
33910                 // iframe?
33911                 b.el.select('iframe',true).setSize(width,height);
33912                 
33913             }, this);
33914             
33915             for (var i = 0; i < this.cols; i++){
33916                 
33917                 if(maxY[i] < maxY[col]){
33918                     col = i;
33919                     continue;
33920                 }
33921                 
33922                 col = Math.min(col, i);
33923                 
33924             }
33925             
33926             x = pos.x + col * (this.colWidth + this.padWidth);
33927             
33928             y = maxY[col];
33929             
33930             var positions = [];
33931             
33932             switch (box.length){
33933                 case 1 :
33934                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33935                     break;
33936                 case 2 :
33937                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33938                     break;
33939                 case 3 :
33940                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33941                     break;
33942                 case 4 :
33943                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33944                     break;
33945                 default :
33946                     break;
33947             }
33948             
33949             Roo.each(box, function(b,kk){
33950                 
33951                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33952                 
33953                 var sz = b.el.getSize();
33954                 
33955                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33956                 
33957             }, this);
33958             
33959         }, this);
33960         
33961         var mY = 0;
33962         
33963         for (var i = 0; i < this.cols; i++){
33964             mY = Math.max(mY, maxY[i]);
33965         }
33966         
33967         this.el.setHeight(mY - pos.y);
33968         
33969     },
33970     
33971 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33972 //    {
33973 //        var pos = this.el.getBox(true);
33974 //        var x = pos.x;
33975 //        var y = pos.y;
33976 //        var maxX = pos.right;
33977 //        
33978 //        var maxHeight = 0;
33979 //        
33980 //        Roo.each(items, function(item, k){
33981 //            
33982 //            var c = k % 2;
33983 //            
33984 //            item.el.position('absolute');
33985 //                
33986 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33987 //
33988 //            item.el.setWidth(width);
33989 //
33990 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33991 //
33992 //            item.el.setHeight(height);
33993 //            
33994 //            if(c == 0){
33995 //                item.el.setXY([x, y], isInstant ? false : true);
33996 //            } else {
33997 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33998 //            }
33999 //            
34000 //            y = y + height + this.alternativePadWidth;
34001 //            
34002 //            maxHeight = maxHeight + height + this.alternativePadWidth;
34003 //            
34004 //        }, this);
34005 //        
34006 //        this.el.setHeight(maxHeight);
34007 //        
34008 //    },
34009     
34010     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34011     {
34012         var pos = this.el.getBox(true);
34013         
34014         var minX = pos.x;
34015         var minY = pos.y;
34016         
34017         var maxX = pos.right;
34018         
34019         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34020         
34021         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34022         
34023         Roo.each(queue, function(box, k){
34024             
34025             Roo.each(box, function(b, kk){
34026                 
34027                 b.el.position('absolute');
34028                 
34029                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34030                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34031                 
34032                 if(b.size == 'md-left' || b.size == 'md-right'){
34033                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34034                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34035                 }
34036                 
34037                 b.el.setWidth(width);
34038                 b.el.setHeight(height);
34039                 
34040             }, this);
34041             
34042             if(!box.length){
34043                 return;
34044             }
34045             
34046             var positions = [];
34047             
34048             switch (box.length){
34049                 case 1 :
34050                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34051                     break;
34052                 case 2 :
34053                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34054                     break;
34055                 case 3 :
34056                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34057                     break;
34058                 case 4 :
34059                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34060                     break;
34061                 default :
34062                     break;
34063             }
34064             
34065             Roo.each(box, function(b,kk){
34066                 
34067                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34068                 
34069                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34070                 
34071             }, this);
34072             
34073         }, this);
34074         
34075     },
34076     
34077     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34078     {
34079         Roo.each(eItems, function(b,k){
34080             
34081             b.size = (k == 0) ? 'sm' : 'xs';
34082             b.x = (k == 0) ? 2 : 1;
34083             b.y = (k == 0) ? 2 : 1;
34084             
34085             b.el.position('absolute');
34086             
34087             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34088                 
34089             b.el.setWidth(width);
34090             
34091             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34092             
34093             b.el.setHeight(height);
34094             
34095         }, this);
34096
34097         var positions = [];
34098         
34099         positions.push({
34100             x : maxX - this.unitWidth * 2 - this.gutter,
34101             y : minY
34102         });
34103         
34104         positions.push({
34105             x : maxX - this.unitWidth,
34106             y : minY + (this.unitWidth + this.gutter) * 2
34107         });
34108         
34109         positions.push({
34110             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34111             y : minY
34112         });
34113         
34114         Roo.each(eItems, function(b,k){
34115             
34116             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34117
34118         }, this);
34119         
34120     },
34121     
34122     getVerticalOneBoxColPositions : function(x, y, box)
34123     {
34124         var pos = [];
34125         
34126         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34127         
34128         if(box[0].size == 'md-left'){
34129             rand = 0;
34130         }
34131         
34132         if(box[0].size == 'md-right'){
34133             rand = 1;
34134         }
34135         
34136         pos.push({
34137             x : x + (this.unitWidth + this.gutter) * rand,
34138             y : y
34139         });
34140         
34141         return pos;
34142     },
34143     
34144     getVerticalTwoBoxColPositions : function(x, y, box)
34145     {
34146         var pos = [];
34147         
34148         if(box[0].size == 'xs'){
34149             
34150             pos.push({
34151                 x : x,
34152                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34153             });
34154
34155             pos.push({
34156                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34157                 y : y
34158             });
34159             
34160             return pos;
34161             
34162         }
34163         
34164         pos.push({
34165             x : x,
34166             y : y
34167         });
34168
34169         pos.push({
34170             x : x + (this.unitWidth + this.gutter) * 2,
34171             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34172         });
34173         
34174         return pos;
34175         
34176     },
34177     
34178     getVerticalThreeBoxColPositions : function(x, y, box)
34179     {
34180         var pos = [];
34181         
34182         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34183             
34184             pos.push({
34185                 x : x,
34186                 y : y
34187             });
34188
34189             pos.push({
34190                 x : x + (this.unitWidth + this.gutter) * 1,
34191                 y : y
34192             });
34193             
34194             pos.push({
34195                 x : x + (this.unitWidth + this.gutter) * 2,
34196                 y : y
34197             });
34198             
34199             return pos;
34200             
34201         }
34202         
34203         if(box[0].size == 'xs' && box[1].size == 'xs'){
34204             
34205             pos.push({
34206                 x : x,
34207                 y : y
34208             });
34209
34210             pos.push({
34211                 x : x,
34212                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34213             });
34214             
34215             pos.push({
34216                 x : x + (this.unitWidth + this.gutter) * 1,
34217                 y : y
34218             });
34219             
34220             return pos;
34221             
34222         }
34223         
34224         pos.push({
34225             x : x,
34226             y : y
34227         });
34228
34229         pos.push({
34230             x : x + (this.unitWidth + this.gutter) * 2,
34231             y : y
34232         });
34233
34234         pos.push({
34235             x : x + (this.unitWidth + this.gutter) * 2,
34236             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34237         });
34238             
34239         return pos;
34240         
34241     },
34242     
34243     getVerticalFourBoxColPositions : function(x, y, box)
34244     {
34245         var pos = [];
34246         
34247         if(box[0].size == 'xs'){
34248             
34249             pos.push({
34250                 x : x,
34251                 y : y
34252             });
34253
34254             pos.push({
34255                 x : x,
34256                 y : y + (this.unitHeight + this.gutter) * 1
34257             });
34258             
34259             pos.push({
34260                 x : x,
34261                 y : y + (this.unitHeight + this.gutter) * 2
34262             });
34263             
34264             pos.push({
34265                 x : x + (this.unitWidth + this.gutter) * 1,
34266                 y : y
34267             });
34268             
34269             return pos;
34270             
34271         }
34272         
34273         pos.push({
34274             x : x,
34275             y : y
34276         });
34277
34278         pos.push({
34279             x : x + (this.unitWidth + this.gutter) * 2,
34280             y : y
34281         });
34282
34283         pos.push({
34284             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34285             y : y + (this.unitHeight + this.gutter) * 1
34286         });
34287
34288         pos.push({
34289             x : x + (this.unitWidth + this.gutter) * 2,
34290             y : y + (this.unitWidth + this.gutter) * 2
34291         });
34292
34293         return pos;
34294         
34295     },
34296     
34297     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34298     {
34299         var pos = [];
34300         
34301         if(box[0].size == 'md-left'){
34302             pos.push({
34303                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34304                 y : minY
34305             });
34306             
34307             return pos;
34308         }
34309         
34310         if(box[0].size == 'md-right'){
34311             pos.push({
34312                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34313                 y : minY + (this.unitWidth + this.gutter) * 1
34314             });
34315             
34316             return pos;
34317         }
34318         
34319         var rand = Math.floor(Math.random() * (4 - box[0].y));
34320         
34321         pos.push({
34322             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34323             y : minY + (this.unitWidth + this.gutter) * rand
34324         });
34325         
34326         return pos;
34327         
34328     },
34329     
34330     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34331     {
34332         var pos = [];
34333         
34334         if(box[0].size == 'xs'){
34335             
34336             pos.push({
34337                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34338                 y : minY
34339             });
34340
34341             pos.push({
34342                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34343                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34344             });
34345             
34346             return pos;
34347             
34348         }
34349         
34350         pos.push({
34351             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34352             y : minY
34353         });
34354
34355         pos.push({
34356             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34357             y : minY + (this.unitWidth + this.gutter) * 2
34358         });
34359         
34360         return pos;
34361         
34362     },
34363     
34364     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34365     {
34366         var pos = [];
34367         
34368         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34369             
34370             pos.push({
34371                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34372                 y : minY
34373             });
34374
34375             pos.push({
34376                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34377                 y : minY + (this.unitWidth + this.gutter) * 1
34378             });
34379             
34380             pos.push({
34381                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34382                 y : minY + (this.unitWidth + this.gutter) * 2
34383             });
34384             
34385             return pos;
34386             
34387         }
34388         
34389         if(box[0].size == 'xs' && box[1].size == 'xs'){
34390             
34391             pos.push({
34392                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34393                 y : minY
34394             });
34395
34396             pos.push({
34397                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34398                 y : minY
34399             });
34400             
34401             pos.push({
34402                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34403                 y : minY + (this.unitWidth + this.gutter) * 1
34404             });
34405             
34406             return pos;
34407             
34408         }
34409         
34410         pos.push({
34411             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34412             y : minY
34413         });
34414
34415         pos.push({
34416             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34417             y : minY + (this.unitWidth + this.gutter) * 2
34418         });
34419
34420         pos.push({
34421             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34422             y : minY + (this.unitWidth + this.gutter) * 2
34423         });
34424             
34425         return pos;
34426         
34427     },
34428     
34429     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34430     {
34431         var pos = [];
34432         
34433         if(box[0].size == 'xs'){
34434             
34435             pos.push({
34436                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34437                 y : minY
34438             });
34439
34440             pos.push({
34441                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34442                 y : minY
34443             });
34444             
34445             pos.push({
34446                 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),
34447                 y : minY
34448             });
34449             
34450             pos.push({
34451                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34452                 y : minY + (this.unitWidth + this.gutter) * 1
34453             });
34454             
34455             return pos;
34456             
34457         }
34458         
34459         pos.push({
34460             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34461             y : minY
34462         });
34463         
34464         pos.push({
34465             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34466             y : minY + (this.unitWidth + this.gutter) * 2
34467         });
34468         
34469         pos.push({
34470             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34471             y : minY + (this.unitWidth + this.gutter) * 2
34472         });
34473         
34474         pos.push({
34475             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),
34476             y : minY + (this.unitWidth + this.gutter) * 2
34477         });
34478
34479         return pos;
34480         
34481     },
34482     
34483     /**
34484     * remove a Masonry Brick
34485     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34486     */
34487     removeBrick : function(brick_id)
34488     {
34489         if (!brick_id) {
34490             return;
34491         }
34492         
34493         for (var i = 0; i<this.bricks.length; i++) {
34494             if (this.bricks[i].id == brick_id) {
34495                 this.bricks.splice(i,1);
34496                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34497                 this.initial();
34498             }
34499         }
34500     },
34501     
34502     /**
34503     * adds a Masonry Brick
34504     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34505     */
34506     addBrick : function(cfg)
34507     {
34508         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34509         //this.register(cn);
34510         cn.parentId = this.id;
34511         cn.render(this.el);
34512         return cn;
34513     },
34514     
34515     /**
34516     * register a Masonry Brick
34517     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34518     */
34519     
34520     register : function(brick)
34521     {
34522         this.bricks.push(brick);
34523         brick.masonryId = this.id;
34524     },
34525     
34526     /**
34527     * clear all the Masonry Brick
34528     */
34529     clearAll : function()
34530     {
34531         this.bricks = [];
34532         //this.getChildContainer().dom.innerHTML = "";
34533         this.el.dom.innerHTML = '';
34534     },
34535     
34536     getSelected : function()
34537     {
34538         if (!this.selectedBrick) {
34539             return false;
34540         }
34541         
34542         return this.selectedBrick;
34543     }
34544 });
34545
34546 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34547     
34548     groups: {},
34549      /**
34550     * register a Masonry Layout
34551     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34552     */
34553     
34554     register : function(layout)
34555     {
34556         this.groups[layout.id] = layout;
34557     },
34558     /**
34559     * fetch a  Masonry Layout based on the masonry layout ID
34560     * @param {string} the masonry layout to add
34561     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34562     */
34563     
34564     get: function(layout_id) {
34565         if (typeof(this.groups[layout_id]) == 'undefined') {
34566             return false;
34567         }
34568         return this.groups[layout_id] ;
34569     }
34570     
34571     
34572     
34573 });
34574
34575  
34576
34577  /**
34578  *
34579  * This is based on 
34580  * http://masonry.desandro.com
34581  *
34582  * The idea is to render all the bricks based on vertical width...
34583  *
34584  * The original code extends 'outlayer' - we might need to use that....
34585  * 
34586  */
34587
34588
34589 /**
34590  * @class Roo.bootstrap.LayoutMasonryAuto
34591  * @extends Roo.bootstrap.Component
34592  * Bootstrap Layout Masonry class
34593  * 
34594  * @constructor
34595  * Create a new Element
34596  * @param {Object} config The config object
34597  */
34598
34599 Roo.bootstrap.LayoutMasonryAuto = function(config){
34600     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34601 };
34602
34603 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34604     
34605       /**
34606      * @cfg {Boolean} isFitWidth  - resize the width..
34607      */   
34608     isFitWidth : false,  // options..
34609     /**
34610      * @cfg {Boolean} isOriginLeft = left align?
34611      */   
34612     isOriginLeft : true,
34613     /**
34614      * @cfg {Boolean} isOriginTop = top align?
34615      */   
34616     isOriginTop : false,
34617     /**
34618      * @cfg {Boolean} isLayoutInstant = no animation?
34619      */   
34620     isLayoutInstant : false, // needed?
34621     /**
34622      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34623      */   
34624     isResizingContainer : true,
34625     /**
34626      * @cfg {Number} columnWidth  width of the columns 
34627      */   
34628     
34629     columnWidth : 0,
34630     
34631     /**
34632      * @cfg {Number} maxCols maximum number of columns
34633      */   
34634     
34635     maxCols: 0,
34636     /**
34637      * @cfg {Number} padHeight padding below box..
34638      */   
34639     
34640     padHeight : 10, 
34641     
34642     /**
34643      * @cfg {Boolean} isAutoInitial defalut true
34644      */   
34645     
34646     isAutoInitial : true, 
34647     
34648     // private?
34649     gutter : 0,
34650     
34651     containerWidth: 0,
34652     initialColumnWidth : 0,
34653     currentSize : null,
34654     
34655     colYs : null, // array.
34656     maxY : 0,
34657     padWidth: 10,
34658     
34659     
34660     tag: 'div',
34661     cls: '',
34662     bricks: null, //CompositeElement
34663     cols : 0, // array?
34664     // element : null, // wrapped now this.el
34665     _isLayoutInited : null, 
34666     
34667     
34668     getAutoCreate : function(){
34669         
34670         var cfg = {
34671             tag: this.tag,
34672             cls: 'blog-masonary-wrapper ' + this.cls,
34673             cn : {
34674                 cls : 'mas-boxes masonary'
34675             }
34676         };
34677         
34678         return cfg;
34679     },
34680     
34681     getChildContainer: function( )
34682     {
34683         if (this.boxesEl) {
34684             return this.boxesEl;
34685         }
34686         
34687         this.boxesEl = this.el.select('.mas-boxes').first();
34688         
34689         return this.boxesEl;
34690     },
34691     
34692     
34693     initEvents : function()
34694     {
34695         var _this = this;
34696         
34697         if(this.isAutoInitial){
34698             Roo.log('hook children rendered');
34699             this.on('childrenrendered', function() {
34700                 Roo.log('children rendered');
34701                 _this.initial();
34702             } ,this);
34703         }
34704         
34705     },
34706     
34707     initial : function()
34708     {
34709         this.reloadItems();
34710
34711         this.currentSize = this.el.getBox(true);
34712
34713         /// was window resize... - let's see if this works..
34714         Roo.EventManager.onWindowResize(this.resize, this); 
34715
34716         if(!this.isAutoInitial){
34717             this.layout();
34718             return;
34719         }
34720         
34721         this.layout.defer(500,this);
34722     },
34723     
34724     reloadItems: function()
34725     {
34726         this.bricks = this.el.select('.masonry-brick', true);
34727         
34728         this.bricks.each(function(b) {
34729             //Roo.log(b.getSize());
34730             if (!b.attr('originalwidth')) {
34731                 b.attr('originalwidth',  b.getSize().width);
34732             }
34733             
34734         });
34735         
34736         Roo.log(this.bricks.elements.length);
34737     },
34738     
34739     resize : function()
34740     {
34741         Roo.log('resize');
34742         var cs = this.el.getBox(true);
34743         
34744         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34745             Roo.log("no change in with or X");
34746             return;
34747         }
34748         this.currentSize = cs;
34749         this.layout();
34750     },
34751     
34752     layout : function()
34753     {
34754          Roo.log('layout');
34755         this._resetLayout();
34756         //this._manageStamps();
34757       
34758         // don't animate first layout
34759         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34760         this.layoutItems( isInstant );
34761       
34762         // flag for initalized
34763         this._isLayoutInited = true;
34764     },
34765     
34766     layoutItems : function( isInstant )
34767     {
34768         //var items = this._getItemsForLayout( this.items );
34769         // original code supports filtering layout items.. we just ignore it..
34770         
34771         this._layoutItems( this.bricks , isInstant );
34772       
34773         this._postLayout();
34774     },
34775     _layoutItems : function ( items , isInstant)
34776     {
34777        //this.fireEvent( 'layout', this, items );
34778     
34779
34780         if ( !items || !items.elements.length ) {
34781           // no items, emit event with empty array
34782             return;
34783         }
34784
34785         var queue = [];
34786         items.each(function(item) {
34787             Roo.log("layout item");
34788             Roo.log(item);
34789             // get x/y object from method
34790             var position = this._getItemLayoutPosition( item );
34791             // enqueue
34792             position.item = item;
34793             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34794             queue.push( position );
34795         }, this);
34796       
34797         this._processLayoutQueue( queue );
34798     },
34799     /** Sets position of item in DOM
34800     * @param {Element} item
34801     * @param {Number} x - horizontal position
34802     * @param {Number} y - vertical position
34803     * @param {Boolean} isInstant - disables transitions
34804     */
34805     _processLayoutQueue : function( queue )
34806     {
34807         for ( var i=0, len = queue.length; i < len; i++ ) {
34808             var obj = queue[i];
34809             obj.item.position('absolute');
34810             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34811         }
34812     },
34813       
34814     
34815     /**
34816     * Any logic you want to do after each layout,
34817     * i.e. size the container
34818     */
34819     _postLayout : function()
34820     {
34821         this.resizeContainer();
34822     },
34823     
34824     resizeContainer : function()
34825     {
34826         if ( !this.isResizingContainer ) {
34827             return;
34828         }
34829         var size = this._getContainerSize();
34830         if ( size ) {
34831             this.el.setSize(size.width,size.height);
34832             this.boxesEl.setSize(size.width,size.height);
34833         }
34834     },
34835     
34836     
34837     
34838     _resetLayout : function()
34839     {
34840         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34841         this.colWidth = this.el.getWidth();
34842         //this.gutter = this.el.getWidth(); 
34843         
34844         this.measureColumns();
34845
34846         // reset column Y
34847         var i = this.cols;
34848         this.colYs = [];
34849         while (i--) {
34850             this.colYs.push( 0 );
34851         }
34852     
34853         this.maxY = 0;
34854     },
34855
34856     measureColumns : function()
34857     {
34858         this.getContainerWidth();
34859       // if columnWidth is 0, default to outerWidth of first item
34860         if ( !this.columnWidth ) {
34861             var firstItem = this.bricks.first();
34862             Roo.log(firstItem);
34863             this.columnWidth  = this.containerWidth;
34864             if (firstItem && firstItem.attr('originalwidth') ) {
34865                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34866             }
34867             // columnWidth fall back to item of first element
34868             Roo.log("set column width?");
34869                         this.initialColumnWidth = this.columnWidth  ;
34870
34871             // if first elem has no width, default to size of container
34872             
34873         }
34874         
34875         
34876         if (this.initialColumnWidth) {
34877             this.columnWidth = this.initialColumnWidth;
34878         }
34879         
34880         
34881             
34882         // column width is fixed at the top - however if container width get's smaller we should
34883         // reduce it...
34884         
34885         // this bit calcs how man columns..
34886             
34887         var columnWidth = this.columnWidth += this.gutter;
34888       
34889         // calculate columns
34890         var containerWidth = this.containerWidth + this.gutter;
34891         
34892         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34893         // fix rounding errors, typically with gutters
34894         var excess = columnWidth - containerWidth % columnWidth;
34895         
34896         
34897         // if overshoot is less than a pixel, round up, otherwise floor it
34898         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34899         cols = Math[ mathMethod ]( cols );
34900         this.cols = Math.max( cols, 1 );
34901         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34902         
34903          // padding positioning..
34904         var totalColWidth = this.cols * this.columnWidth;
34905         var padavail = this.containerWidth - totalColWidth;
34906         // so for 2 columns - we need 3 'pads'
34907         
34908         var padNeeded = (1+this.cols) * this.padWidth;
34909         
34910         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34911         
34912         this.columnWidth += padExtra
34913         //this.padWidth = Math.floor(padavail /  ( this.cols));
34914         
34915         // adjust colum width so that padding is fixed??
34916         
34917         // we have 3 columns ... total = width * 3
34918         // we have X left over... that should be used by 
34919         
34920         //if (this.expandC) {
34921             
34922         //}
34923         
34924         
34925         
34926     },
34927     
34928     getContainerWidth : function()
34929     {
34930        /* // container is parent if fit width
34931         var container = this.isFitWidth ? this.element.parentNode : this.element;
34932         // check that this.size and size are there
34933         // IE8 triggers resize on body size change, so they might not be
34934         
34935         var size = getSize( container );  //FIXME
34936         this.containerWidth = size && size.innerWidth; //FIXME
34937         */
34938          
34939         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34940         
34941     },
34942     
34943     _getItemLayoutPosition : function( item )  // what is item?
34944     {
34945         // we resize the item to our columnWidth..
34946       
34947         item.setWidth(this.columnWidth);
34948         item.autoBoxAdjust  = false;
34949         
34950         var sz = item.getSize();
34951  
34952         // how many columns does this brick span
34953         var remainder = this.containerWidth % this.columnWidth;
34954         
34955         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34956         // round if off by 1 pixel, otherwise use ceil
34957         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34958         colSpan = Math.min( colSpan, this.cols );
34959         
34960         // normally this should be '1' as we dont' currently allow multi width columns..
34961         
34962         var colGroup = this._getColGroup( colSpan );
34963         // get the minimum Y value from the columns
34964         var minimumY = Math.min.apply( Math, colGroup );
34965         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34966         
34967         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34968          
34969         // position the brick
34970         var position = {
34971             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34972             y: this.currentSize.y + minimumY + this.padHeight
34973         };
34974         
34975         Roo.log(position);
34976         // apply setHeight to necessary columns
34977         var setHeight = minimumY + sz.height + this.padHeight;
34978         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34979         
34980         var setSpan = this.cols + 1 - colGroup.length;
34981         for ( var i = 0; i < setSpan; i++ ) {
34982           this.colYs[ shortColIndex + i ] = setHeight ;
34983         }
34984       
34985         return position;
34986     },
34987     
34988     /**
34989      * @param {Number} colSpan - number of columns the element spans
34990      * @returns {Array} colGroup
34991      */
34992     _getColGroup : function( colSpan )
34993     {
34994         if ( colSpan < 2 ) {
34995           // if brick spans only one column, use all the column Ys
34996           return this.colYs;
34997         }
34998       
34999         var colGroup = [];
35000         // how many different places could this brick fit horizontally
35001         var groupCount = this.cols + 1 - colSpan;
35002         // for each group potential horizontal position
35003         for ( var i = 0; i < groupCount; i++ ) {
35004           // make an array of colY values for that one group
35005           var groupColYs = this.colYs.slice( i, i + colSpan );
35006           // and get the max value of the array
35007           colGroup[i] = Math.max.apply( Math, groupColYs );
35008         }
35009         return colGroup;
35010     },
35011     /*
35012     _manageStamp : function( stamp )
35013     {
35014         var stampSize =  stamp.getSize();
35015         var offset = stamp.getBox();
35016         // get the columns that this stamp affects
35017         var firstX = this.isOriginLeft ? offset.x : offset.right;
35018         var lastX = firstX + stampSize.width;
35019         var firstCol = Math.floor( firstX / this.columnWidth );
35020         firstCol = Math.max( 0, firstCol );
35021         
35022         var lastCol = Math.floor( lastX / this.columnWidth );
35023         // lastCol should not go over if multiple of columnWidth #425
35024         lastCol -= lastX % this.columnWidth ? 0 : 1;
35025         lastCol = Math.min( this.cols - 1, lastCol );
35026         
35027         // set colYs to bottom of the stamp
35028         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35029             stampSize.height;
35030             
35031         for ( var i = firstCol; i <= lastCol; i++ ) {
35032           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35033         }
35034     },
35035     */
35036     
35037     _getContainerSize : function()
35038     {
35039         this.maxY = Math.max.apply( Math, this.colYs );
35040         var size = {
35041             height: this.maxY
35042         };
35043       
35044         if ( this.isFitWidth ) {
35045             size.width = this._getContainerFitWidth();
35046         }
35047       
35048         return size;
35049     },
35050     
35051     _getContainerFitWidth : function()
35052     {
35053         var unusedCols = 0;
35054         // count unused columns
35055         var i = this.cols;
35056         while ( --i ) {
35057           if ( this.colYs[i] !== 0 ) {
35058             break;
35059           }
35060           unusedCols++;
35061         }
35062         // fit container to columns that have been used
35063         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35064     },
35065     
35066     needsResizeLayout : function()
35067     {
35068         var previousWidth = this.containerWidth;
35069         this.getContainerWidth();
35070         return previousWidth !== this.containerWidth;
35071     }
35072  
35073 });
35074
35075  
35076
35077  /*
35078  * - LGPL
35079  *
35080  * element
35081  * 
35082  */
35083
35084 /**
35085  * @class Roo.bootstrap.MasonryBrick
35086  * @extends Roo.bootstrap.Component
35087  * Bootstrap MasonryBrick class
35088  * 
35089  * @constructor
35090  * Create a new MasonryBrick
35091  * @param {Object} config The config object
35092  */
35093
35094 Roo.bootstrap.MasonryBrick = function(config){
35095     
35096     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35097     
35098     Roo.bootstrap.MasonryBrick.register(this);
35099     
35100     this.addEvents({
35101         // raw events
35102         /**
35103          * @event click
35104          * When a MasonryBrick is clcik
35105          * @param {Roo.bootstrap.MasonryBrick} this
35106          * @param {Roo.EventObject} e
35107          */
35108         "click" : true
35109     });
35110 };
35111
35112 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35113     
35114     /**
35115      * @cfg {String} title
35116      */   
35117     title : '',
35118     /**
35119      * @cfg {String} html
35120      */   
35121     html : '',
35122     /**
35123      * @cfg {String} bgimage
35124      */   
35125     bgimage : '',
35126     /**
35127      * @cfg {String} videourl
35128      */   
35129     videourl : '',
35130     /**
35131      * @cfg {String} cls
35132      */   
35133     cls : '',
35134     /**
35135      * @cfg {String} href
35136      */   
35137     href : '',
35138     /**
35139      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35140      */   
35141     size : 'xs',
35142     
35143     /**
35144      * @cfg {String} placetitle (center|bottom)
35145      */   
35146     placetitle : '',
35147     
35148     /**
35149      * @cfg {Boolean} isFitContainer defalut true
35150      */   
35151     isFitContainer : true, 
35152     
35153     /**
35154      * @cfg {Boolean} preventDefault defalut false
35155      */   
35156     preventDefault : false, 
35157     
35158     /**
35159      * @cfg {Boolean} inverse defalut false
35160      */   
35161     maskInverse : false, 
35162     
35163     getAutoCreate : function()
35164     {
35165         if(!this.isFitContainer){
35166             return this.getSplitAutoCreate();
35167         }
35168         
35169         var cls = 'masonry-brick masonry-brick-full';
35170         
35171         if(this.href.length){
35172             cls += ' masonry-brick-link';
35173         }
35174         
35175         if(this.bgimage.length){
35176             cls += ' masonry-brick-image';
35177         }
35178         
35179         if(this.maskInverse){
35180             cls += ' mask-inverse';
35181         }
35182         
35183         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35184             cls += ' enable-mask';
35185         }
35186         
35187         if(this.size){
35188             cls += ' masonry-' + this.size + '-brick';
35189         }
35190         
35191         if(this.placetitle.length){
35192             
35193             switch (this.placetitle) {
35194                 case 'center' :
35195                     cls += ' masonry-center-title';
35196                     break;
35197                 case 'bottom' :
35198                     cls += ' masonry-bottom-title';
35199                     break;
35200                 default:
35201                     break;
35202             }
35203             
35204         } else {
35205             if(!this.html.length && !this.bgimage.length){
35206                 cls += ' masonry-center-title';
35207             }
35208
35209             if(!this.html.length && this.bgimage.length){
35210                 cls += ' masonry-bottom-title';
35211             }
35212         }
35213         
35214         if(this.cls){
35215             cls += ' ' + this.cls;
35216         }
35217         
35218         var cfg = {
35219             tag: (this.href.length) ? 'a' : 'div',
35220             cls: cls,
35221             cn: [
35222                 {
35223                     tag: 'div',
35224                     cls: 'masonry-brick-mask'
35225                 },
35226                 {
35227                     tag: 'div',
35228                     cls: 'masonry-brick-paragraph',
35229                     cn: []
35230                 }
35231             ]
35232         };
35233         
35234         if(this.href.length){
35235             cfg.href = this.href;
35236         }
35237         
35238         var cn = cfg.cn[1].cn;
35239         
35240         if(this.title.length){
35241             cn.push({
35242                 tag: 'h4',
35243                 cls: 'masonry-brick-title',
35244                 html: this.title
35245             });
35246         }
35247         
35248         if(this.html.length){
35249             cn.push({
35250                 tag: 'p',
35251                 cls: 'masonry-brick-text',
35252                 html: this.html
35253             });
35254         }
35255         
35256         if (!this.title.length && !this.html.length) {
35257             cfg.cn[1].cls += ' hide';
35258         }
35259         
35260         if(this.bgimage.length){
35261             cfg.cn.push({
35262                 tag: 'img',
35263                 cls: 'masonry-brick-image-view',
35264                 src: this.bgimage
35265             });
35266         }
35267         
35268         if(this.videourl.length){
35269             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35270             // youtube support only?
35271             cfg.cn.push({
35272                 tag: 'iframe',
35273                 cls: 'masonry-brick-image-view',
35274                 src: vurl,
35275                 frameborder : 0,
35276                 allowfullscreen : true
35277             });
35278         }
35279         
35280         return cfg;
35281         
35282     },
35283     
35284     getSplitAutoCreate : function()
35285     {
35286         var cls = 'masonry-brick masonry-brick-split';
35287         
35288         if(this.href.length){
35289             cls += ' masonry-brick-link';
35290         }
35291         
35292         if(this.bgimage.length){
35293             cls += ' masonry-brick-image';
35294         }
35295         
35296         if(this.size){
35297             cls += ' masonry-' + this.size + '-brick';
35298         }
35299         
35300         switch (this.placetitle) {
35301             case 'center' :
35302                 cls += ' masonry-center-title';
35303                 break;
35304             case 'bottom' :
35305                 cls += ' masonry-bottom-title';
35306                 break;
35307             default:
35308                 if(!this.bgimage.length){
35309                     cls += ' masonry-center-title';
35310                 }
35311
35312                 if(this.bgimage.length){
35313                     cls += ' masonry-bottom-title';
35314                 }
35315                 break;
35316         }
35317         
35318         if(this.cls){
35319             cls += ' ' + this.cls;
35320         }
35321         
35322         var cfg = {
35323             tag: (this.href.length) ? 'a' : 'div',
35324             cls: cls,
35325             cn: [
35326                 {
35327                     tag: 'div',
35328                     cls: 'masonry-brick-split-head',
35329                     cn: [
35330                         {
35331                             tag: 'div',
35332                             cls: 'masonry-brick-paragraph',
35333                             cn: []
35334                         }
35335                     ]
35336                 },
35337                 {
35338                     tag: 'div',
35339                     cls: 'masonry-brick-split-body',
35340                     cn: []
35341                 }
35342             ]
35343         };
35344         
35345         if(this.href.length){
35346             cfg.href = this.href;
35347         }
35348         
35349         if(this.title.length){
35350             cfg.cn[0].cn[0].cn.push({
35351                 tag: 'h4',
35352                 cls: 'masonry-brick-title',
35353                 html: this.title
35354             });
35355         }
35356         
35357         if(this.html.length){
35358             cfg.cn[1].cn.push({
35359                 tag: 'p',
35360                 cls: 'masonry-brick-text',
35361                 html: this.html
35362             });
35363         }
35364
35365         if(this.bgimage.length){
35366             cfg.cn[0].cn.push({
35367                 tag: 'img',
35368                 cls: 'masonry-brick-image-view',
35369                 src: this.bgimage
35370             });
35371         }
35372         
35373         if(this.videourl.length){
35374             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35375             // youtube support only?
35376             cfg.cn[0].cn.cn.push({
35377                 tag: 'iframe',
35378                 cls: 'masonry-brick-image-view',
35379                 src: vurl,
35380                 frameborder : 0,
35381                 allowfullscreen : true
35382             });
35383         }
35384         
35385         return cfg;
35386     },
35387     
35388     initEvents: function() 
35389     {
35390         switch (this.size) {
35391             case 'xs' :
35392                 this.x = 1;
35393                 this.y = 1;
35394                 break;
35395             case 'sm' :
35396                 this.x = 2;
35397                 this.y = 2;
35398                 break;
35399             case 'md' :
35400             case 'md-left' :
35401             case 'md-right' :
35402                 this.x = 3;
35403                 this.y = 3;
35404                 break;
35405             case 'tall' :
35406                 this.x = 2;
35407                 this.y = 3;
35408                 break;
35409             case 'wide' :
35410                 this.x = 3;
35411                 this.y = 2;
35412                 break;
35413             case 'wide-thin' :
35414                 this.x = 3;
35415                 this.y = 1;
35416                 break;
35417                         
35418             default :
35419                 break;
35420         }
35421         
35422         if(Roo.isTouch){
35423             this.el.on('touchstart', this.onTouchStart, this);
35424             this.el.on('touchmove', this.onTouchMove, this);
35425             this.el.on('touchend', this.onTouchEnd, this);
35426             this.el.on('contextmenu', this.onContextMenu, this);
35427         } else {
35428             this.el.on('mouseenter'  ,this.enter, this);
35429             this.el.on('mouseleave', this.leave, this);
35430             this.el.on('click', this.onClick, this);
35431         }
35432         
35433         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35434             this.parent().bricks.push(this);   
35435         }
35436         
35437     },
35438     
35439     onClick: function(e, el)
35440     {
35441         var time = this.endTimer - this.startTimer;
35442         // Roo.log(e.preventDefault());
35443         if(Roo.isTouch){
35444             if(time > 1000){
35445                 e.preventDefault();
35446                 return;
35447             }
35448         }
35449         
35450         if(!this.preventDefault){
35451             return;
35452         }
35453         
35454         e.preventDefault();
35455         
35456         if (this.activeClass != '') {
35457             this.selectBrick();
35458         }
35459         
35460         this.fireEvent('click', this, e);
35461     },
35462     
35463     enter: 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.9, true);
35473         }
35474     },
35475     
35476     leave: function(e, el)
35477     {
35478         e.preventDefault();
35479         
35480         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35481             return;
35482         }
35483         
35484         if(this.bgimage.length && this.html.length){
35485             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35486         }
35487     },
35488     
35489     onTouchStart: function(e, el)
35490     {
35491 //        e.preventDefault();
35492         
35493         this.touchmoved = false;
35494         
35495         if(!this.isFitContainer){
35496             return;
35497         }
35498         
35499         if(!this.bgimage.length || !this.html.length){
35500             return;
35501         }
35502         
35503         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35504         
35505         this.timer = new Date().getTime();
35506         
35507     },
35508     
35509     onTouchMove: function(e, el)
35510     {
35511         this.touchmoved = true;
35512     },
35513     
35514     onContextMenu : function(e,el)
35515     {
35516         e.preventDefault();
35517         e.stopPropagation();
35518         return false;
35519     },
35520     
35521     onTouchEnd: function(e, el)
35522     {
35523 //        e.preventDefault();
35524         
35525         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35526         
35527             this.leave(e,el);
35528             
35529             return;
35530         }
35531         
35532         if(!this.bgimage.length || !this.html.length){
35533             
35534             if(this.href.length){
35535                 window.location.href = this.href;
35536             }
35537             
35538             return;
35539         }
35540         
35541         if(!this.isFitContainer){
35542             return;
35543         }
35544         
35545         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35546         
35547         window.location.href = this.href;
35548     },
35549     
35550     //selection on single brick only
35551     selectBrick : function() {
35552         
35553         if (!this.parentId) {
35554             return;
35555         }
35556         
35557         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35558         var index = m.selectedBrick.indexOf(this.id);
35559         
35560         if ( index > -1) {
35561             m.selectedBrick.splice(index,1);
35562             this.el.removeClass(this.activeClass);
35563             return;
35564         }
35565         
35566         for(var i = 0; i < m.selectedBrick.length; i++) {
35567             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35568             b.el.removeClass(b.activeClass);
35569         }
35570         
35571         m.selectedBrick = [];
35572         
35573         m.selectedBrick.push(this.id);
35574         this.el.addClass(this.activeClass);
35575         return;
35576     },
35577     
35578     isSelected : function(){
35579         return this.el.hasClass(this.activeClass);
35580         
35581     }
35582 });
35583
35584 Roo.apply(Roo.bootstrap.MasonryBrick, {
35585     
35586     //groups: {},
35587     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35588      /**
35589     * register a Masonry Brick
35590     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35591     */
35592     
35593     register : function(brick)
35594     {
35595         //this.groups[brick.id] = brick;
35596         this.groups.add(brick.id, brick);
35597     },
35598     /**
35599     * fetch a  masonry brick based on the masonry brick ID
35600     * @param {string} the masonry brick to add
35601     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35602     */
35603     
35604     get: function(brick_id) 
35605     {
35606         // if (typeof(this.groups[brick_id]) == 'undefined') {
35607         //     return false;
35608         // }
35609         // return this.groups[brick_id] ;
35610         
35611         if(this.groups.key(brick_id)) {
35612             return this.groups.key(brick_id);
35613         }
35614         
35615         return false;
35616     }
35617     
35618     
35619     
35620 });
35621
35622  /*
35623  * - LGPL
35624  *
35625  * element
35626  * 
35627  */
35628
35629 /**
35630  * @class Roo.bootstrap.Brick
35631  * @extends Roo.bootstrap.Component
35632  * Bootstrap Brick class
35633  * 
35634  * @constructor
35635  * Create a new Brick
35636  * @param {Object} config The config object
35637  */
35638
35639 Roo.bootstrap.Brick = function(config){
35640     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35641     
35642     this.addEvents({
35643         // raw events
35644         /**
35645          * @event click
35646          * When a Brick is click
35647          * @param {Roo.bootstrap.Brick} this
35648          * @param {Roo.EventObject} e
35649          */
35650         "click" : true
35651     });
35652 };
35653
35654 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35655     
35656     /**
35657      * @cfg {String} title
35658      */   
35659     title : '',
35660     /**
35661      * @cfg {String} html
35662      */   
35663     html : '',
35664     /**
35665      * @cfg {String} bgimage
35666      */   
35667     bgimage : '',
35668     /**
35669      * @cfg {String} cls
35670      */   
35671     cls : '',
35672     /**
35673      * @cfg {String} href
35674      */   
35675     href : '',
35676     /**
35677      * @cfg {String} video
35678      */   
35679     video : '',
35680     /**
35681      * @cfg {Boolean} square
35682      */   
35683     square : true,
35684     
35685     getAutoCreate : function()
35686     {
35687         var cls = 'roo-brick';
35688         
35689         if(this.href.length){
35690             cls += ' roo-brick-link';
35691         }
35692         
35693         if(this.bgimage.length){
35694             cls += ' roo-brick-image';
35695         }
35696         
35697         if(!this.html.length && !this.bgimage.length){
35698             cls += ' roo-brick-center-title';
35699         }
35700         
35701         if(!this.html.length && this.bgimage.length){
35702             cls += ' roo-brick-bottom-title';
35703         }
35704         
35705         if(this.cls){
35706             cls += ' ' + this.cls;
35707         }
35708         
35709         var cfg = {
35710             tag: (this.href.length) ? 'a' : 'div',
35711             cls: cls,
35712             cn: [
35713                 {
35714                     tag: 'div',
35715                     cls: 'roo-brick-paragraph',
35716                     cn: []
35717                 }
35718             ]
35719         };
35720         
35721         if(this.href.length){
35722             cfg.href = this.href;
35723         }
35724         
35725         var cn = cfg.cn[0].cn;
35726         
35727         if(this.title.length){
35728             cn.push({
35729                 tag: 'h4',
35730                 cls: 'roo-brick-title',
35731                 html: this.title
35732             });
35733         }
35734         
35735         if(this.html.length){
35736             cn.push({
35737                 tag: 'p',
35738                 cls: 'roo-brick-text',
35739                 html: this.html
35740             });
35741         } else {
35742             cn.cls += ' hide';
35743         }
35744         
35745         if(this.bgimage.length){
35746             cfg.cn.push({
35747                 tag: 'img',
35748                 cls: 'roo-brick-image-view',
35749                 src: this.bgimage
35750             });
35751         }
35752         
35753         return cfg;
35754     },
35755     
35756     initEvents: function() 
35757     {
35758         if(this.title.length || this.html.length){
35759             this.el.on('mouseenter'  ,this.enter, this);
35760             this.el.on('mouseleave', this.leave, this);
35761         }
35762         
35763         Roo.EventManager.onWindowResize(this.resize, this); 
35764         
35765         if(this.bgimage.length){
35766             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35767             this.imageEl.on('load', this.onImageLoad, this);
35768             return;
35769         }
35770         
35771         this.resize();
35772     },
35773     
35774     onImageLoad : function()
35775     {
35776         this.resize();
35777     },
35778     
35779     resize : function()
35780     {
35781         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35782         
35783         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35784         
35785         if(this.bgimage.length){
35786             var image = this.el.select('.roo-brick-image-view', true).first();
35787             
35788             image.setWidth(paragraph.getWidth());
35789             
35790             if(this.square){
35791                 image.setHeight(paragraph.getWidth());
35792             }
35793             
35794             this.el.setHeight(image.getHeight());
35795             paragraph.setHeight(image.getHeight());
35796             
35797         }
35798         
35799     },
35800     
35801     enter: function(e, el)
35802     {
35803         e.preventDefault();
35804         
35805         if(this.bgimage.length){
35806             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35807             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35808         }
35809     },
35810     
35811     leave: function(e, el)
35812     {
35813         e.preventDefault();
35814         
35815         if(this.bgimage.length){
35816             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35817             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35818         }
35819     }
35820     
35821 });
35822
35823  
35824
35825  /*
35826  * - LGPL
35827  *
35828  * Number field 
35829  */
35830
35831 /**
35832  * @class Roo.bootstrap.NumberField
35833  * @extends Roo.bootstrap.Input
35834  * Bootstrap NumberField class
35835  * 
35836  * 
35837  * 
35838  * 
35839  * @constructor
35840  * Create a new NumberField
35841  * @param {Object} config The config object
35842  */
35843
35844 Roo.bootstrap.NumberField = function(config){
35845     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35846 };
35847
35848 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35849     
35850     /**
35851      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35852      */
35853     allowDecimals : true,
35854     /**
35855      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35856      */
35857     decimalSeparator : ".",
35858     /**
35859      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35860      */
35861     decimalPrecision : 2,
35862     /**
35863      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35864      */
35865     allowNegative : true,
35866     
35867     /**
35868      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35869      */
35870     allowZero: true,
35871     /**
35872      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35873      */
35874     minValue : Number.NEGATIVE_INFINITY,
35875     /**
35876      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35877      */
35878     maxValue : Number.MAX_VALUE,
35879     /**
35880      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35881      */
35882     minText : "The minimum value for this field is {0}",
35883     /**
35884      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35885      */
35886     maxText : "The maximum value for this field is {0}",
35887     /**
35888      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35889      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35890      */
35891     nanText : "{0} is not a valid number",
35892     /**
35893      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35894      */
35895     thousandsDelimiter : false,
35896     /**
35897      * @cfg {String} valueAlign alignment of value
35898      */
35899     valueAlign : "left",
35900
35901     getAutoCreate : function()
35902     {
35903         var hiddenInput = {
35904             tag: 'input',
35905             type: 'hidden',
35906             id: Roo.id(),
35907             cls: 'hidden-number-input'
35908         };
35909         
35910         if (this.name) {
35911             hiddenInput.name = this.name;
35912         }
35913         
35914         this.name = '';
35915         
35916         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35917         
35918         this.name = hiddenInput.name;
35919         
35920         if(cfg.cn.length > 0) {
35921             cfg.cn.push(hiddenInput);
35922         }
35923         
35924         return cfg;
35925     },
35926
35927     // private
35928     initEvents : function()
35929     {   
35930         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35931         
35932         var allowed = "0123456789";
35933         
35934         if(this.allowDecimals){
35935             allowed += this.decimalSeparator;
35936         }
35937         
35938         if(this.allowNegative){
35939             allowed += "-";
35940         }
35941         
35942         if(this.thousandsDelimiter) {
35943             allowed += ",";
35944         }
35945         
35946         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35947         
35948         var keyPress = function(e){
35949             
35950             var k = e.getKey();
35951             
35952             var c = e.getCharCode();
35953             
35954             if(
35955                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35956                     allowed.indexOf(String.fromCharCode(c)) === -1
35957             ){
35958                 e.stopEvent();
35959                 return;
35960             }
35961             
35962             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35963                 return;
35964             }
35965             
35966             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35967                 e.stopEvent();
35968             }
35969         };
35970         
35971         this.el.on("keypress", keyPress, this);
35972     },
35973     
35974     validateValue : function(value)
35975     {
35976         
35977         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35978             return false;
35979         }
35980         
35981         var num = this.parseValue(value);
35982         
35983         if(isNaN(num)){
35984             this.markInvalid(String.format(this.nanText, value));
35985             return false;
35986         }
35987         
35988         if(num < this.minValue){
35989             this.markInvalid(String.format(this.minText, this.minValue));
35990             return false;
35991         }
35992         
35993         if(num > this.maxValue){
35994             this.markInvalid(String.format(this.maxText, this.maxValue));
35995             return false;
35996         }
35997         
35998         return true;
35999     },
36000
36001     getValue : function()
36002     {
36003         var v = this.hiddenEl().getValue();
36004         
36005         return this.fixPrecision(this.parseValue(v));
36006     },
36007
36008     parseValue : function(value)
36009     {
36010         if(this.thousandsDelimiter) {
36011             value += "";
36012             r = new RegExp(",", "g");
36013             value = value.replace(r, "");
36014         }
36015         
36016         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36017         return isNaN(value) ? '' : value;
36018     },
36019
36020     fixPrecision : function(value)
36021     {
36022         if(this.thousandsDelimiter) {
36023             value += "";
36024             r = new RegExp(",", "g");
36025             value = value.replace(r, "");
36026         }
36027         
36028         var nan = isNaN(value);
36029         
36030         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36031             return nan ? '' : value;
36032         }
36033         return parseFloat(value).toFixed(this.decimalPrecision);
36034     },
36035
36036     setValue : function(v)
36037     {
36038         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36039         
36040         this.value = v;
36041         
36042         if(this.rendered){
36043             
36044             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36045             
36046             this.inputEl().dom.value = (v == '') ? '' :
36047                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36048             
36049             if(!this.allowZero && v === '0') {
36050                 this.hiddenEl().dom.value = '';
36051                 this.inputEl().dom.value = '';
36052             }
36053             
36054             this.validate();
36055         }
36056     },
36057
36058     decimalPrecisionFcn : function(v)
36059     {
36060         return Math.floor(v);
36061     },
36062
36063     beforeBlur : function()
36064     {
36065         var v = this.parseValue(this.getRawValue());
36066         
36067         if(v || v === 0 || v === ''){
36068             this.setValue(v);
36069         }
36070     },
36071     
36072     hiddenEl : function()
36073     {
36074         return this.el.select('input.hidden-number-input',true).first();
36075     }
36076     
36077 });
36078
36079  
36080
36081 /*
36082 * Licence: LGPL
36083 */
36084
36085 /**
36086  * @class Roo.bootstrap.DocumentSlider
36087  * @extends Roo.bootstrap.Component
36088  * Bootstrap DocumentSlider class
36089  * 
36090  * @constructor
36091  * Create a new DocumentViewer
36092  * @param {Object} config The config object
36093  */
36094
36095 Roo.bootstrap.DocumentSlider = function(config){
36096     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36097     
36098     this.files = [];
36099     
36100     this.addEvents({
36101         /**
36102          * @event initial
36103          * Fire after initEvent
36104          * @param {Roo.bootstrap.DocumentSlider} this
36105          */
36106         "initial" : true,
36107         /**
36108          * @event update
36109          * Fire after update
36110          * @param {Roo.bootstrap.DocumentSlider} this
36111          */
36112         "update" : true,
36113         /**
36114          * @event click
36115          * Fire after click
36116          * @param {Roo.bootstrap.DocumentSlider} this
36117          */
36118         "click" : true
36119     });
36120 };
36121
36122 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36123     
36124     files : false,
36125     
36126     indicator : 0,
36127     
36128     getAutoCreate : function()
36129     {
36130         var cfg = {
36131             tag : 'div',
36132             cls : 'roo-document-slider',
36133             cn : [
36134                 {
36135                     tag : 'div',
36136                     cls : 'roo-document-slider-header',
36137                     cn : [
36138                         {
36139                             tag : 'div',
36140                             cls : 'roo-document-slider-header-title'
36141                         }
36142                     ]
36143                 },
36144                 {
36145                     tag : 'div',
36146                     cls : 'roo-document-slider-body',
36147                     cn : [
36148                         {
36149                             tag : 'div',
36150                             cls : 'roo-document-slider-prev',
36151                             cn : [
36152                                 {
36153                                     tag : 'i',
36154                                     cls : 'fa fa-chevron-left'
36155                                 }
36156                             ]
36157                         },
36158                         {
36159                             tag : 'div',
36160                             cls : 'roo-document-slider-thumb',
36161                             cn : [
36162                                 {
36163                                     tag : 'img',
36164                                     cls : 'roo-document-slider-image'
36165                                 }
36166                             ]
36167                         },
36168                         {
36169                             tag : 'div',
36170                             cls : 'roo-document-slider-next',
36171                             cn : [
36172                                 {
36173                                     tag : 'i',
36174                                     cls : 'fa fa-chevron-right'
36175                                 }
36176                             ]
36177                         }
36178                     ]
36179                 }
36180             ]
36181         };
36182         
36183         return cfg;
36184     },
36185     
36186     initEvents : function()
36187     {
36188         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36189         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36190         
36191         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36192         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36193         
36194         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36195         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36196         
36197         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36198         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36199         
36200         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36201         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36202         
36203         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36204         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36205         
36206         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36207         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36208         
36209         this.thumbEl.on('click', this.onClick, this);
36210         
36211         this.prevIndicator.on('click', this.prev, this);
36212         
36213         this.nextIndicator.on('click', this.next, this);
36214         
36215     },
36216     
36217     initial : function()
36218     {
36219         if(this.files.length){
36220             this.indicator = 1;
36221             this.update()
36222         }
36223         
36224         this.fireEvent('initial', this);
36225     },
36226     
36227     update : function()
36228     {
36229         this.imageEl.attr('src', this.files[this.indicator - 1]);
36230         
36231         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36232         
36233         this.prevIndicator.show();
36234         
36235         if(this.indicator == 1){
36236             this.prevIndicator.hide();
36237         }
36238         
36239         this.nextIndicator.show();
36240         
36241         if(this.indicator == this.files.length){
36242             this.nextIndicator.hide();
36243         }
36244         
36245         this.thumbEl.scrollTo('top');
36246         
36247         this.fireEvent('update', this);
36248     },
36249     
36250     onClick : function(e)
36251     {
36252         e.preventDefault();
36253         
36254         this.fireEvent('click', this);
36255     },
36256     
36257     prev : function(e)
36258     {
36259         e.preventDefault();
36260         
36261         this.indicator = Math.max(1, this.indicator - 1);
36262         
36263         this.update();
36264     },
36265     
36266     next : function(e)
36267     {
36268         e.preventDefault();
36269         
36270         this.indicator = Math.min(this.files.length, this.indicator + 1);
36271         
36272         this.update();
36273     }
36274 });
36275 /*
36276  * - LGPL
36277  *
36278  * RadioSet
36279  *
36280  *
36281  */
36282
36283 /**
36284  * @class Roo.bootstrap.RadioSet
36285  * @extends Roo.bootstrap.Input
36286  * Bootstrap RadioSet class
36287  * @cfg {String} indicatorpos (left|right) default left
36288  * @cfg {Boolean} inline (true|false) inline the element (default true)
36289  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36290  * @constructor
36291  * Create a new RadioSet
36292  * @param {Object} config The config object
36293  */
36294
36295 Roo.bootstrap.RadioSet = function(config){
36296     
36297     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36298     
36299     this.radioes = [];
36300     
36301     Roo.bootstrap.RadioSet.register(this);
36302     
36303     this.addEvents({
36304         /**
36305         * @event check
36306         * Fires when the element is checked or unchecked.
36307         * @param {Roo.bootstrap.RadioSet} this This radio
36308         * @param {Roo.bootstrap.Radio} item The checked item
36309         */
36310        check : true,
36311        /**
36312         * @event click
36313         * Fires when the element is click.
36314         * @param {Roo.bootstrap.RadioSet} this This radio set
36315         * @param {Roo.bootstrap.Radio} item The checked item
36316         * @param {Roo.EventObject} e The event object
36317         */
36318        click : true
36319     });
36320     
36321 };
36322
36323 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36324
36325     radioes : false,
36326     
36327     inline : true,
36328     
36329     weight : '',
36330     
36331     indicatorpos : 'left',
36332     
36333     getAutoCreate : function()
36334     {
36335         var label = {
36336             tag : 'label',
36337             cls : 'roo-radio-set-label',
36338             cn : [
36339                 {
36340                     tag : 'span',
36341                     html : this.fieldLabel
36342                 }
36343             ]
36344         };
36345         if (Roo.bootstrap.version == 3) {
36346             
36347             
36348             if(this.indicatorpos == 'left'){
36349                 label.cn.unshift({
36350                     tag : 'i',
36351                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36352                     tooltip : 'This field is required'
36353                 });
36354             } else {
36355                 label.cn.push({
36356                     tag : 'i',
36357                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36358                     tooltip : 'This field is required'
36359                 });
36360             }
36361         }
36362         var items = {
36363             tag : 'div',
36364             cls : 'roo-radio-set-items'
36365         };
36366         
36367         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36368         
36369         if (align === 'left' && this.fieldLabel.length) {
36370             
36371             items = {
36372                 cls : "roo-radio-set-right", 
36373                 cn: [
36374                     items
36375                 ]
36376             };
36377             
36378             if(this.labelWidth > 12){
36379                 label.style = "width: " + this.labelWidth + 'px';
36380             }
36381             
36382             if(this.labelWidth < 13 && this.labelmd == 0){
36383                 this.labelmd = this.labelWidth;
36384             }
36385             
36386             if(this.labellg > 0){
36387                 label.cls += ' col-lg-' + this.labellg;
36388                 items.cls += ' col-lg-' + (12 - this.labellg);
36389             }
36390             
36391             if(this.labelmd > 0){
36392                 label.cls += ' col-md-' + this.labelmd;
36393                 items.cls += ' col-md-' + (12 - this.labelmd);
36394             }
36395             
36396             if(this.labelsm > 0){
36397                 label.cls += ' col-sm-' + this.labelsm;
36398                 items.cls += ' col-sm-' + (12 - this.labelsm);
36399             }
36400             
36401             if(this.labelxs > 0){
36402                 label.cls += ' col-xs-' + this.labelxs;
36403                 items.cls += ' col-xs-' + (12 - this.labelxs);
36404             }
36405         }
36406         
36407         var cfg = {
36408             tag : 'div',
36409             cls : 'roo-radio-set',
36410             cn : [
36411                 {
36412                     tag : 'input',
36413                     cls : 'roo-radio-set-input',
36414                     type : 'hidden',
36415                     name : this.name,
36416                     value : this.value ? this.value :  ''
36417                 },
36418                 label,
36419                 items
36420             ]
36421         };
36422         
36423         if(this.weight.length){
36424             cfg.cls += ' roo-radio-' + this.weight;
36425         }
36426         
36427         if(this.inline) {
36428             cfg.cls += ' roo-radio-set-inline';
36429         }
36430         
36431         var settings=this;
36432         ['xs','sm','md','lg'].map(function(size){
36433             if (settings[size]) {
36434                 cfg.cls += ' col-' + size + '-' + settings[size];
36435             }
36436         });
36437         
36438         return cfg;
36439         
36440     },
36441
36442     initEvents : function()
36443     {
36444         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36445         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36446         
36447         if(!this.fieldLabel.length){
36448             this.labelEl.hide();
36449         }
36450         
36451         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36452         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36453         
36454         this.indicator = this.indicatorEl();
36455         
36456         if(this.indicator){
36457             this.indicator.addClass('invisible');
36458         }
36459         
36460         this.originalValue = this.getValue();
36461         
36462     },
36463     
36464     inputEl: function ()
36465     {
36466         return this.el.select('.roo-radio-set-input', true).first();
36467     },
36468     
36469     getChildContainer : function()
36470     {
36471         return this.itemsEl;
36472     },
36473     
36474     register : function(item)
36475     {
36476         this.radioes.push(item);
36477         
36478     },
36479     
36480     validate : function()
36481     {   
36482         if(this.getVisibilityEl().hasClass('hidden')){
36483             return true;
36484         }
36485         
36486         var valid = false;
36487         
36488         Roo.each(this.radioes, function(i){
36489             if(!i.checked){
36490                 return;
36491             }
36492             
36493             valid = true;
36494             return false;
36495         });
36496         
36497         if(this.allowBlank) {
36498             return true;
36499         }
36500         
36501         if(this.disabled || valid){
36502             this.markValid();
36503             return true;
36504         }
36505         
36506         this.markInvalid();
36507         return false;
36508         
36509     },
36510     
36511     markValid : function()
36512     {
36513         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36514             this.indicatorEl().removeClass('visible');
36515             this.indicatorEl().addClass('invisible');
36516         }
36517         
36518         
36519         if (Roo.bootstrap.version == 3) {
36520             this.el.removeClass([this.invalidClass, this.validClass]);
36521             this.el.addClass(this.validClass);
36522         } else {
36523             this.el.removeClass(['is-invalid','is-valid']);
36524             this.el.addClass(['is-valid']);
36525         }
36526         this.fireEvent('valid', this);
36527     },
36528     
36529     markInvalid : function(msg)
36530     {
36531         if(this.allowBlank || this.disabled){
36532             return;
36533         }
36534         
36535         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36536             this.indicatorEl().removeClass('invisible');
36537             this.indicatorEl().addClass('visible');
36538         }
36539         if (Roo.bootstrap.version == 3) {
36540             this.el.removeClass([this.invalidClass, this.validClass]);
36541             this.el.addClass(this.invalidClass);
36542         } else {
36543             this.el.removeClass(['is-invalid','is-valid']);
36544             this.el.addClass(['is-invalid']);
36545         }
36546         
36547         this.fireEvent('invalid', this, msg);
36548         
36549     },
36550     
36551     setValue : function(v, suppressEvent)
36552     {   
36553         if(this.value === v){
36554             return;
36555         }
36556         
36557         this.value = v;
36558         
36559         if(this.rendered){
36560             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36561         }
36562         
36563         Roo.each(this.radioes, function(i){
36564             i.checked = false;
36565             i.el.removeClass('checked');
36566         });
36567         
36568         Roo.each(this.radioes, function(i){
36569             
36570             if(i.value === v || i.value.toString() === v.toString()){
36571                 i.checked = true;
36572                 i.el.addClass('checked');
36573                 
36574                 if(suppressEvent !== true){
36575                     this.fireEvent('check', this, i);
36576                 }
36577                 
36578                 return false;
36579             }
36580             
36581         }, this);
36582         
36583         this.validate();
36584     },
36585     
36586     clearInvalid : function(){
36587         
36588         if(!this.el || this.preventMark){
36589             return;
36590         }
36591         
36592         this.el.removeClass([this.invalidClass]);
36593         
36594         this.fireEvent('valid', this);
36595     }
36596     
36597 });
36598
36599 Roo.apply(Roo.bootstrap.RadioSet, {
36600     
36601     groups: {},
36602     
36603     register : function(set)
36604     {
36605         this.groups[set.name] = set;
36606     },
36607     
36608     get: function(name) 
36609     {
36610         if (typeof(this.groups[name]) == 'undefined') {
36611             return false;
36612         }
36613         
36614         return this.groups[name] ;
36615     }
36616     
36617 });
36618 /*
36619  * Based on:
36620  * Ext JS Library 1.1.1
36621  * Copyright(c) 2006-2007, Ext JS, LLC.
36622  *
36623  * Originally Released Under LGPL - original licence link has changed is not relivant.
36624  *
36625  * Fork - LGPL
36626  * <script type="text/javascript">
36627  */
36628
36629
36630 /**
36631  * @class Roo.bootstrap.SplitBar
36632  * @extends Roo.util.Observable
36633  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36634  * <br><br>
36635  * Usage:
36636  * <pre><code>
36637 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36638                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36639 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36640 split.minSize = 100;
36641 split.maxSize = 600;
36642 split.animate = true;
36643 split.on('moved', splitterMoved);
36644 </code></pre>
36645  * @constructor
36646  * Create a new SplitBar
36647  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36648  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36649  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36650  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36651                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36652                         position of the SplitBar).
36653  */
36654 Roo.bootstrap.SplitBar = function(cfg){
36655     
36656     /** @private */
36657     
36658     //{
36659     //  dragElement : elm
36660     //  resizingElement: el,
36661         // optional..
36662     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36663     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36664         // existingProxy ???
36665     //}
36666     
36667     this.el = Roo.get(cfg.dragElement, true);
36668     this.el.dom.unselectable = "on";
36669     /** @private */
36670     this.resizingEl = Roo.get(cfg.resizingElement, true);
36671
36672     /**
36673      * @private
36674      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36675      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36676      * @type Number
36677      */
36678     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36679     
36680     /**
36681      * The minimum size of the resizing element. (Defaults to 0)
36682      * @type Number
36683      */
36684     this.minSize = 0;
36685     
36686     /**
36687      * The maximum size of the resizing element. (Defaults to 2000)
36688      * @type Number
36689      */
36690     this.maxSize = 2000;
36691     
36692     /**
36693      * Whether to animate the transition to the new size
36694      * @type Boolean
36695      */
36696     this.animate = false;
36697     
36698     /**
36699      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36700      * @type Boolean
36701      */
36702     this.useShim = false;
36703     
36704     /** @private */
36705     this.shim = null;
36706     
36707     if(!cfg.existingProxy){
36708         /** @private */
36709         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36710     }else{
36711         this.proxy = Roo.get(cfg.existingProxy).dom;
36712     }
36713     /** @private */
36714     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36715     
36716     /** @private */
36717     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36718     
36719     /** @private */
36720     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36721     
36722     /** @private */
36723     this.dragSpecs = {};
36724     
36725     /**
36726      * @private The adapter to use to positon and resize elements
36727      */
36728     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36729     this.adapter.init(this);
36730     
36731     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36732         /** @private */
36733         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36734         this.el.addClass("roo-splitbar-h");
36735     }else{
36736         /** @private */
36737         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36738         this.el.addClass("roo-splitbar-v");
36739     }
36740     
36741     this.addEvents({
36742         /**
36743          * @event resize
36744          * Fires when the splitter is moved (alias for {@link #event-moved})
36745          * @param {Roo.bootstrap.SplitBar} this
36746          * @param {Number} newSize the new width or height
36747          */
36748         "resize" : true,
36749         /**
36750          * @event moved
36751          * Fires when the splitter is moved
36752          * @param {Roo.bootstrap.SplitBar} this
36753          * @param {Number} newSize the new width or height
36754          */
36755         "moved" : true,
36756         /**
36757          * @event beforeresize
36758          * Fires before the splitter is dragged
36759          * @param {Roo.bootstrap.SplitBar} this
36760          */
36761         "beforeresize" : true,
36762
36763         "beforeapply" : true
36764     });
36765
36766     Roo.util.Observable.call(this);
36767 };
36768
36769 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36770     onStartProxyDrag : function(x, y){
36771         this.fireEvent("beforeresize", this);
36772         if(!this.overlay){
36773             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36774             o.unselectable();
36775             o.enableDisplayMode("block");
36776             // all splitbars share the same overlay
36777             Roo.bootstrap.SplitBar.prototype.overlay = o;
36778         }
36779         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36780         this.overlay.show();
36781         Roo.get(this.proxy).setDisplayed("block");
36782         var size = this.adapter.getElementSize(this);
36783         this.activeMinSize = this.getMinimumSize();;
36784         this.activeMaxSize = this.getMaximumSize();;
36785         var c1 = size - this.activeMinSize;
36786         var c2 = Math.max(this.activeMaxSize - size, 0);
36787         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36788             this.dd.resetConstraints();
36789             this.dd.setXConstraint(
36790                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36791                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36792             );
36793             this.dd.setYConstraint(0, 0);
36794         }else{
36795             this.dd.resetConstraints();
36796             this.dd.setXConstraint(0, 0);
36797             this.dd.setYConstraint(
36798                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36799                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36800             );
36801          }
36802         this.dragSpecs.startSize = size;
36803         this.dragSpecs.startPoint = [x, y];
36804         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36805     },
36806     
36807     /** 
36808      * @private Called after the drag operation by the DDProxy
36809      */
36810     onEndProxyDrag : function(e){
36811         Roo.get(this.proxy).setDisplayed(false);
36812         var endPoint = Roo.lib.Event.getXY(e);
36813         if(this.overlay){
36814             this.overlay.hide();
36815         }
36816         var newSize;
36817         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36818             newSize = this.dragSpecs.startSize + 
36819                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36820                     endPoint[0] - this.dragSpecs.startPoint[0] :
36821                     this.dragSpecs.startPoint[0] - endPoint[0]
36822                 );
36823         }else{
36824             newSize = this.dragSpecs.startSize + 
36825                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36826                     endPoint[1] - this.dragSpecs.startPoint[1] :
36827                     this.dragSpecs.startPoint[1] - endPoint[1]
36828                 );
36829         }
36830         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36831         if(newSize != this.dragSpecs.startSize){
36832             if(this.fireEvent('beforeapply', this, newSize) !== false){
36833                 this.adapter.setElementSize(this, newSize);
36834                 this.fireEvent("moved", this, newSize);
36835                 this.fireEvent("resize", this, newSize);
36836             }
36837         }
36838     },
36839     
36840     /**
36841      * Get the adapter this SplitBar uses
36842      * @return The adapter object
36843      */
36844     getAdapter : function(){
36845         return this.adapter;
36846     },
36847     
36848     /**
36849      * Set the adapter this SplitBar uses
36850      * @param {Object} adapter A SplitBar adapter object
36851      */
36852     setAdapter : function(adapter){
36853         this.adapter = adapter;
36854         this.adapter.init(this);
36855     },
36856     
36857     /**
36858      * Gets the minimum size for the resizing element
36859      * @return {Number} The minimum size
36860      */
36861     getMinimumSize : function(){
36862         return this.minSize;
36863     },
36864     
36865     /**
36866      * Sets the minimum size for the resizing element
36867      * @param {Number} minSize The minimum size
36868      */
36869     setMinimumSize : function(minSize){
36870         this.minSize = minSize;
36871     },
36872     
36873     /**
36874      * Gets the maximum size for the resizing element
36875      * @return {Number} The maximum size
36876      */
36877     getMaximumSize : function(){
36878         return this.maxSize;
36879     },
36880     
36881     /**
36882      * Sets the maximum size for the resizing element
36883      * @param {Number} maxSize The maximum size
36884      */
36885     setMaximumSize : function(maxSize){
36886         this.maxSize = maxSize;
36887     },
36888     
36889     /**
36890      * Sets the initialize size for the resizing element
36891      * @param {Number} size The initial size
36892      */
36893     setCurrentSize : function(size){
36894         var oldAnimate = this.animate;
36895         this.animate = false;
36896         this.adapter.setElementSize(this, size);
36897         this.animate = oldAnimate;
36898     },
36899     
36900     /**
36901      * Destroy this splitbar. 
36902      * @param {Boolean} removeEl True to remove the element
36903      */
36904     destroy : function(removeEl){
36905         if(this.shim){
36906             this.shim.remove();
36907         }
36908         this.dd.unreg();
36909         this.proxy.parentNode.removeChild(this.proxy);
36910         if(removeEl){
36911             this.el.remove();
36912         }
36913     }
36914 });
36915
36916 /**
36917  * @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.
36918  */
36919 Roo.bootstrap.SplitBar.createProxy = function(dir){
36920     var proxy = new Roo.Element(document.createElement("div"));
36921     proxy.unselectable();
36922     var cls = 'roo-splitbar-proxy';
36923     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36924     document.body.appendChild(proxy.dom);
36925     return proxy.dom;
36926 };
36927
36928 /** 
36929  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36930  * Default Adapter. It assumes the splitter and resizing element are not positioned
36931  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36932  */
36933 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36934 };
36935
36936 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36937     // do nothing for now
36938     init : function(s){
36939     
36940     },
36941     /**
36942      * Called before drag operations to get the current size of the resizing element. 
36943      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36944      */
36945      getElementSize : function(s){
36946         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36947             return s.resizingEl.getWidth();
36948         }else{
36949             return s.resizingEl.getHeight();
36950         }
36951     },
36952     
36953     /**
36954      * Called after drag operations to set the size of the resizing element.
36955      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36956      * @param {Number} newSize The new size to set
36957      * @param {Function} onComplete A function to be invoked when resizing is complete
36958      */
36959     setElementSize : function(s, newSize, onComplete){
36960         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36961             if(!s.animate){
36962                 s.resizingEl.setWidth(newSize);
36963                 if(onComplete){
36964                     onComplete(s, newSize);
36965                 }
36966             }else{
36967                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36968             }
36969         }else{
36970             
36971             if(!s.animate){
36972                 s.resizingEl.setHeight(newSize);
36973                 if(onComplete){
36974                     onComplete(s, newSize);
36975                 }
36976             }else{
36977                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36978             }
36979         }
36980     }
36981 };
36982
36983 /** 
36984  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36985  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36986  * Adapter that  moves the splitter element to align with the resized sizing element. 
36987  * Used with an absolute positioned SplitBar.
36988  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36989  * document.body, make sure you assign an id to the body element.
36990  */
36991 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36992     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36993     this.container = Roo.get(container);
36994 };
36995
36996 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36997     init : function(s){
36998         this.basic.init(s);
36999     },
37000     
37001     getElementSize : function(s){
37002         return this.basic.getElementSize(s);
37003     },
37004     
37005     setElementSize : function(s, newSize, onComplete){
37006         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37007     },
37008     
37009     moveSplitter : function(s){
37010         var yes = Roo.bootstrap.SplitBar;
37011         switch(s.placement){
37012             case yes.LEFT:
37013                 s.el.setX(s.resizingEl.getRight());
37014                 break;
37015             case yes.RIGHT:
37016                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37017                 break;
37018             case yes.TOP:
37019                 s.el.setY(s.resizingEl.getBottom());
37020                 break;
37021             case yes.BOTTOM:
37022                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37023                 break;
37024         }
37025     }
37026 };
37027
37028 /**
37029  * Orientation constant - Create a vertical SplitBar
37030  * @static
37031  * @type Number
37032  */
37033 Roo.bootstrap.SplitBar.VERTICAL = 1;
37034
37035 /**
37036  * Orientation constant - Create a horizontal SplitBar
37037  * @static
37038  * @type Number
37039  */
37040 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37041
37042 /**
37043  * Placement constant - The resizing element is to the left of the splitter element
37044  * @static
37045  * @type Number
37046  */
37047 Roo.bootstrap.SplitBar.LEFT = 1;
37048
37049 /**
37050  * Placement constant - The resizing element is to the right of the splitter element
37051  * @static
37052  * @type Number
37053  */
37054 Roo.bootstrap.SplitBar.RIGHT = 2;
37055
37056 /**
37057  * Placement constant - The resizing element is positioned above the splitter element
37058  * @static
37059  * @type Number
37060  */
37061 Roo.bootstrap.SplitBar.TOP = 3;
37062
37063 /**
37064  * Placement constant - The resizing element is positioned under splitter element
37065  * @static
37066  * @type Number
37067  */
37068 Roo.bootstrap.SplitBar.BOTTOM = 4;
37069 Roo.namespace("Roo.bootstrap.layout");/*
37070  * Based on:
37071  * Ext JS Library 1.1.1
37072  * Copyright(c) 2006-2007, Ext JS, LLC.
37073  *
37074  * Originally Released Under LGPL - original licence link has changed is not relivant.
37075  *
37076  * Fork - LGPL
37077  * <script type="text/javascript">
37078  */
37079
37080 /**
37081  * @class Roo.bootstrap.layout.Manager
37082  * @extends Roo.bootstrap.Component
37083  * Base class for layout managers.
37084  */
37085 Roo.bootstrap.layout.Manager = function(config)
37086 {
37087     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37088
37089
37090
37091
37092
37093     /** false to disable window resize monitoring @type Boolean */
37094     this.monitorWindowResize = true;
37095     this.regions = {};
37096     this.addEvents({
37097         /**
37098          * @event layout
37099          * Fires when a layout is performed.
37100          * @param {Roo.LayoutManager} this
37101          */
37102         "layout" : true,
37103         /**
37104          * @event regionresized
37105          * Fires when the user resizes a region.
37106          * @param {Roo.LayoutRegion} region The resized region
37107          * @param {Number} newSize The new size (width for east/west, height for north/south)
37108          */
37109         "regionresized" : true,
37110         /**
37111          * @event regioncollapsed
37112          * Fires when a region is collapsed.
37113          * @param {Roo.LayoutRegion} region The collapsed region
37114          */
37115         "regioncollapsed" : true,
37116         /**
37117          * @event regionexpanded
37118          * Fires when a region is expanded.
37119          * @param {Roo.LayoutRegion} region The expanded region
37120          */
37121         "regionexpanded" : true
37122     });
37123     this.updating = false;
37124
37125     if (config.el) {
37126         this.el = Roo.get(config.el);
37127         this.initEvents();
37128     }
37129
37130 };
37131
37132 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37133
37134
37135     regions : null,
37136
37137     monitorWindowResize : true,
37138
37139
37140     updating : false,
37141
37142
37143     onRender : function(ct, position)
37144     {
37145         if(!this.el){
37146             this.el = Roo.get(ct);
37147             this.initEvents();
37148         }
37149         //this.fireEvent('render',this);
37150     },
37151
37152
37153     initEvents: function()
37154     {
37155
37156
37157         // ie scrollbar fix
37158         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37159             document.body.scroll = "no";
37160         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37161             this.el.position('relative');
37162         }
37163         this.id = this.el.id;
37164         this.el.addClass("roo-layout-container");
37165         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37166         if(this.el.dom != document.body ) {
37167             this.el.on('resize', this.layout,this);
37168             this.el.on('show', this.layout,this);
37169         }
37170
37171     },
37172
37173     /**
37174      * Returns true if this layout is currently being updated
37175      * @return {Boolean}
37176      */
37177     isUpdating : function(){
37178         return this.updating;
37179     },
37180
37181     /**
37182      * Suspend the LayoutManager from doing auto-layouts while
37183      * making multiple add or remove calls
37184      */
37185     beginUpdate : function(){
37186         this.updating = true;
37187     },
37188
37189     /**
37190      * Restore auto-layouts and optionally disable the manager from performing a layout
37191      * @param {Boolean} noLayout true to disable a layout update
37192      */
37193     endUpdate : function(noLayout){
37194         this.updating = false;
37195         if(!noLayout){
37196             this.layout();
37197         }
37198     },
37199
37200     layout: function(){
37201         // abstract...
37202     },
37203
37204     onRegionResized : function(region, newSize){
37205         this.fireEvent("regionresized", region, newSize);
37206         this.layout();
37207     },
37208
37209     onRegionCollapsed : function(region){
37210         this.fireEvent("regioncollapsed", region);
37211     },
37212
37213     onRegionExpanded : function(region){
37214         this.fireEvent("regionexpanded", region);
37215     },
37216
37217     /**
37218      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37219      * performs box-model adjustments.
37220      * @return {Object} The size as an object {width: (the width), height: (the height)}
37221      */
37222     getViewSize : function()
37223     {
37224         var size;
37225         if(this.el.dom != document.body){
37226             size = this.el.getSize();
37227         }else{
37228             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37229         }
37230         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37231         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37232         return size;
37233     },
37234
37235     /**
37236      * Returns the Element this layout is bound to.
37237      * @return {Roo.Element}
37238      */
37239     getEl : function(){
37240         return this.el;
37241     },
37242
37243     /**
37244      * Returns the specified region.
37245      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37246      * @return {Roo.LayoutRegion}
37247      */
37248     getRegion : function(target){
37249         return this.regions[target.toLowerCase()];
37250     },
37251
37252     onWindowResize : function(){
37253         if(this.monitorWindowResize){
37254             this.layout();
37255         }
37256     }
37257 });
37258 /*
37259  * Based on:
37260  * Ext JS Library 1.1.1
37261  * Copyright(c) 2006-2007, Ext JS, LLC.
37262  *
37263  * Originally Released Under LGPL - original licence link has changed is not relivant.
37264  *
37265  * Fork - LGPL
37266  * <script type="text/javascript">
37267  */
37268 /**
37269  * @class Roo.bootstrap.layout.Border
37270  * @extends Roo.bootstrap.layout.Manager
37271  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37272  * please see: examples/bootstrap/nested.html<br><br>
37273  
37274 <b>The container the layout is rendered into can be either the body element or any other element.
37275 If it is not the body element, the container needs to either be an absolute positioned element,
37276 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37277 the container size if it is not the body element.</b>
37278
37279 * @constructor
37280 * Create a new Border
37281 * @param {Object} config Configuration options
37282  */
37283 Roo.bootstrap.layout.Border = function(config){
37284     config = config || {};
37285     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37286     
37287     
37288     
37289     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37290         if(config[region]){
37291             config[region].region = region;
37292             this.addRegion(config[region]);
37293         }
37294     },this);
37295     
37296 };
37297
37298 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37299
37300 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37301     
37302     parent : false, // this might point to a 'nest' or a ???
37303     
37304     /**
37305      * Creates and adds a new region if it doesn't already exist.
37306      * @param {String} target The target region key (north, south, east, west or center).
37307      * @param {Object} config The regions config object
37308      * @return {BorderLayoutRegion} The new region
37309      */
37310     addRegion : function(config)
37311     {
37312         if(!this.regions[config.region]){
37313             var r = this.factory(config);
37314             this.bindRegion(r);
37315         }
37316         return this.regions[config.region];
37317     },
37318
37319     // private (kinda)
37320     bindRegion : function(r){
37321         this.regions[r.config.region] = r;
37322         
37323         r.on("visibilitychange",    this.layout, this);
37324         r.on("paneladded",          this.layout, this);
37325         r.on("panelremoved",        this.layout, this);
37326         r.on("invalidated",         this.layout, this);
37327         r.on("resized",             this.onRegionResized, this);
37328         r.on("collapsed",           this.onRegionCollapsed, this);
37329         r.on("expanded",            this.onRegionExpanded, this);
37330     },
37331
37332     /**
37333      * Performs a layout update.
37334      */
37335     layout : function()
37336     {
37337         if(this.updating) {
37338             return;
37339         }
37340         
37341         // render all the rebions if they have not been done alreayd?
37342         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37343             if(this.regions[region] && !this.regions[region].bodyEl){
37344                 this.regions[region].onRender(this.el)
37345             }
37346         },this);
37347         
37348         var size = this.getViewSize();
37349         var w = size.width;
37350         var h = size.height;
37351         var centerW = w;
37352         var centerH = h;
37353         var centerY = 0;
37354         var centerX = 0;
37355         //var x = 0, y = 0;
37356
37357         var rs = this.regions;
37358         var north = rs["north"];
37359         var south = rs["south"]; 
37360         var west = rs["west"];
37361         var east = rs["east"];
37362         var center = rs["center"];
37363         //if(this.hideOnLayout){ // not supported anymore
37364             //c.el.setStyle("display", "none");
37365         //}
37366         if(north && north.isVisible()){
37367             var b = north.getBox();
37368             var m = north.getMargins();
37369             b.width = w - (m.left+m.right);
37370             b.x = m.left;
37371             b.y = m.top;
37372             centerY = b.height + b.y + m.bottom;
37373             centerH -= centerY;
37374             north.updateBox(this.safeBox(b));
37375         }
37376         if(south && south.isVisible()){
37377             var b = south.getBox();
37378             var m = south.getMargins();
37379             b.width = w - (m.left+m.right);
37380             b.x = m.left;
37381             var totalHeight = (b.height + m.top + m.bottom);
37382             b.y = h - totalHeight + m.top;
37383             centerH -= totalHeight;
37384             south.updateBox(this.safeBox(b));
37385         }
37386         if(west && west.isVisible()){
37387             var b = west.getBox();
37388             var m = west.getMargins();
37389             b.height = centerH - (m.top+m.bottom);
37390             b.x = m.left;
37391             b.y = centerY + m.top;
37392             var totalWidth = (b.width + m.left + m.right);
37393             centerX += totalWidth;
37394             centerW -= totalWidth;
37395             west.updateBox(this.safeBox(b));
37396         }
37397         if(east && east.isVisible()){
37398             var b = east.getBox();
37399             var m = east.getMargins();
37400             b.height = centerH - (m.top+m.bottom);
37401             var totalWidth = (b.width + m.left + m.right);
37402             b.x = w - totalWidth + m.left;
37403             b.y = centerY + m.top;
37404             centerW -= totalWidth;
37405             east.updateBox(this.safeBox(b));
37406         }
37407         if(center){
37408             var m = center.getMargins();
37409             var centerBox = {
37410                 x: centerX + m.left,
37411                 y: centerY + m.top,
37412                 width: centerW - (m.left+m.right),
37413                 height: centerH - (m.top+m.bottom)
37414             };
37415             //if(this.hideOnLayout){
37416                 //center.el.setStyle("display", "block");
37417             //}
37418             center.updateBox(this.safeBox(centerBox));
37419         }
37420         this.el.repaint();
37421         this.fireEvent("layout", this);
37422     },
37423
37424     // private
37425     safeBox : function(box){
37426         box.width = Math.max(0, box.width);
37427         box.height = Math.max(0, box.height);
37428         return box;
37429     },
37430
37431     /**
37432      * Adds a ContentPanel (or subclass) to this layout.
37433      * @param {String} target The target region key (north, south, east, west or center).
37434      * @param {Roo.ContentPanel} panel The panel to add
37435      * @return {Roo.ContentPanel} The added panel
37436      */
37437     add : function(target, panel){
37438          
37439         target = target.toLowerCase();
37440         return this.regions[target].add(panel);
37441     },
37442
37443     /**
37444      * Remove a ContentPanel (or subclass) to this layout.
37445      * @param {String} target The target region key (north, south, east, west or center).
37446      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37447      * @return {Roo.ContentPanel} The removed panel
37448      */
37449     remove : function(target, panel){
37450         target = target.toLowerCase();
37451         return this.regions[target].remove(panel);
37452     },
37453
37454     /**
37455      * Searches all regions for a panel with the specified id
37456      * @param {String} panelId
37457      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37458      */
37459     findPanel : function(panelId){
37460         var rs = this.regions;
37461         for(var target in rs){
37462             if(typeof rs[target] != "function"){
37463                 var p = rs[target].getPanel(panelId);
37464                 if(p){
37465                     return p;
37466                 }
37467             }
37468         }
37469         return null;
37470     },
37471
37472     /**
37473      * Searches all regions for a panel with the specified id and activates (shows) it.
37474      * @param {String/ContentPanel} panelId The panels id or the panel itself
37475      * @return {Roo.ContentPanel} The shown panel or null
37476      */
37477     showPanel : function(panelId) {
37478       var rs = this.regions;
37479       for(var target in rs){
37480          var r = rs[target];
37481          if(typeof r != "function"){
37482             if(r.hasPanel(panelId)){
37483                return r.showPanel(panelId);
37484             }
37485          }
37486       }
37487       return null;
37488    },
37489
37490    /**
37491      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37492      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37493      */
37494    /*
37495     restoreState : function(provider){
37496         if(!provider){
37497             provider = Roo.state.Manager;
37498         }
37499         var sm = new Roo.LayoutStateManager();
37500         sm.init(this, provider);
37501     },
37502 */
37503  
37504  
37505     /**
37506      * Adds a xtype elements to the layout.
37507      * <pre><code>
37508
37509 layout.addxtype({
37510        xtype : 'ContentPanel',
37511        region: 'west',
37512        items: [ .... ]
37513    }
37514 );
37515
37516 layout.addxtype({
37517         xtype : 'NestedLayoutPanel',
37518         region: 'west',
37519         layout: {
37520            center: { },
37521            west: { }   
37522         },
37523         items : [ ... list of content panels or nested layout panels.. ]
37524    }
37525 );
37526 </code></pre>
37527      * @param {Object} cfg Xtype definition of item to add.
37528      */
37529     addxtype : function(cfg)
37530     {
37531         // basically accepts a pannel...
37532         // can accept a layout region..!?!?
37533         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37534         
37535         
37536         // theory?  children can only be panels??
37537         
37538         //if (!cfg.xtype.match(/Panel$/)) {
37539         //    return false;
37540         //}
37541         var ret = false;
37542         
37543         if (typeof(cfg.region) == 'undefined') {
37544             Roo.log("Failed to add Panel, region was not set");
37545             Roo.log(cfg);
37546             return false;
37547         }
37548         var region = cfg.region;
37549         delete cfg.region;
37550         
37551           
37552         var xitems = [];
37553         if (cfg.items) {
37554             xitems = cfg.items;
37555             delete cfg.items;
37556         }
37557         var nb = false;
37558         
37559         if ( region == 'center') {
37560             Roo.log("Center: " + cfg.title);
37561         }
37562         
37563         
37564         switch(cfg.xtype) 
37565         {
37566             case 'Content':  // ContentPanel (el, cfg)
37567             case 'Scroll':  // ContentPanel (el, cfg)
37568             case 'View': 
37569                 cfg.autoCreate = cfg.autoCreate || true;
37570                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37571                 //} else {
37572                 //    var el = this.el.createChild();
37573                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37574                 //}
37575                 
37576                 this.add(region, ret);
37577                 break;
37578             
37579             /*
37580             case 'TreePanel': // our new panel!
37581                 cfg.el = this.el.createChild();
37582                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37583                 this.add(region, ret);
37584                 break;
37585             */
37586             
37587             case 'Nest': 
37588                 // create a new Layout (which is  a Border Layout...
37589                 
37590                 var clayout = cfg.layout;
37591                 clayout.el  = this.el.createChild();
37592                 clayout.items   = clayout.items  || [];
37593                 
37594                 delete cfg.layout;
37595                 
37596                 // replace this exitems with the clayout ones..
37597                 xitems = clayout.items;
37598                  
37599                 // force background off if it's in center...
37600                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37601                     cfg.background = false;
37602                 }
37603                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37604                 
37605                 
37606                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37607                 //console.log('adding nested layout panel '  + cfg.toSource());
37608                 this.add(region, ret);
37609                 nb = {}; /// find first...
37610                 break;
37611             
37612             case 'Grid':
37613                 
37614                 // needs grid and region
37615                 
37616                 //var el = this.getRegion(region).el.createChild();
37617                 /*
37618                  *var el = this.el.createChild();
37619                 // create the grid first...
37620                 cfg.grid.container = el;
37621                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37622                 */
37623                 
37624                 if (region == 'center' && this.active ) {
37625                     cfg.background = false;
37626                 }
37627                 
37628                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37629                 
37630                 this.add(region, ret);
37631                 /*
37632                 if (cfg.background) {
37633                     // render grid on panel activation (if panel background)
37634                     ret.on('activate', function(gp) {
37635                         if (!gp.grid.rendered) {
37636                     //        gp.grid.render(el);
37637                         }
37638                     });
37639                 } else {
37640                   //  cfg.grid.render(el);
37641                 }
37642                 */
37643                 break;
37644            
37645            
37646             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37647                 // it was the old xcomponent building that caused this before.
37648                 // espeically if border is the top element in the tree.
37649                 ret = this;
37650                 break; 
37651                 
37652                     
37653                 
37654                 
37655                 
37656             default:
37657                 /*
37658                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37659                     
37660                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37661                     this.add(region, ret);
37662                 } else {
37663                 */
37664                     Roo.log(cfg);
37665                     throw "Can not add '" + cfg.xtype + "' to Border";
37666                     return null;
37667              
37668                                 
37669              
37670         }
37671         this.beginUpdate();
37672         // add children..
37673         var region = '';
37674         var abn = {};
37675         Roo.each(xitems, function(i)  {
37676             region = nb && i.region ? i.region : false;
37677             
37678             var add = ret.addxtype(i);
37679            
37680             if (region) {
37681                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37682                 if (!i.background) {
37683                     abn[region] = nb[region] ;
37684                 }
37685             }
37686             
37687         });
37688         this.endUpdate();
37689
37690         // make the last non-background panel active..
37691         //if (nb) { Roo.log(abn); }
37692         if (nb) {
37693             
37694             for(var r in abn) {
37695                 region = this.getRegion(r);
37696                 if (region) {
37697                     // tried using nb[r], but it does not work..
37698                      
37699                     region.showPanel(abn[r]);
37700                    
37701                 }
37702             }
37703         }
37704         return ret;
37705         
37706     },
37707     
37708     
37709 // private
37710     factory : function(cfg)
37711     {
37712         
37713         var validRegions = Roo.bootstrap.layout.Border.regions;
37714
37715         var target = cfg.region;
37716         cfg.mgr = this;
37717         
37718         var r = Roo.bootstrap.layout;
37719         Roo.log(target);
37720         switch(target){
37721             case "north":
37722                 return new r.North(cfg);
37723             case "south":
37724                 return new r.South(cfg);
37725             case "east":
37726                 return new r.East(cfg);
37727             case "west":
37728                 return new r.West(cfg);
37729             case "center":
37730                 return new r.Center(cfg);
37731         }
37732         throw 'Layout region "'+target+'" not supported.';
37733     }
37734     
37735     
37736 });
37737  /*
37738  * Based on:
37739  * Ext JS Library 1.1.1
37740  * Copyright(c) 2006-2007, Ext JS, LLC.
37741  *
37742  * Originally Released Under LGPL - original licence link has changed is not relivant.
37743  *
37744  * Fork - LGPL
37745  * <script type="text/javascript">
37746  */
37747  
37748 /**
37749  * @class Roo.bootstrap.layout.Basic
37750  * @extends Roo.util.Observable
37751  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37752  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37753  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37754  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37755  * @cfg {string}   region  the region that it inhabits..
37756  * @cfg {bool}   skipConfig skip config?
37757  * 
37758
37759  */
37760 Roo.bootstrap.layout.Basic = function(config){
37761     
37762     this.mgr = config.mgr;
37763     
37764     this.position = config.region;
37765     
37766     var skipConfig = config.skipConfig;
37767     
37768     this.events = {
37769         /**
37770          * @scope Roo.BasicLayoutRegion
37771          */
37772         
37773         /**
37774          * @event beforeremove
37775          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37776          * @param {Roo.LayoutRegion} this
37777          * @param {Roo.ContentPanel} panel The panel
37778          * @param {Object} e The cancel event object
37779          */
37780         "beforeremove" : true,
37781         /**
37782          * @event invalidated
37783          * Fires when the layout for this region is changed.
37784          * @param {Roo.LayoutRegion} this
37785          */
37786         "invalidated" : true,
37787         /**
37788          * @event visibilitychange
37789          * Fires when this region is shown or hidden 
37790          * @param {Roo.LayoutRegion} this
37791          * @param {Boolean} visibility true or false
37792          */
37793         "visibilitychange" : true,
37794         /**
37795          * @event paneladded
37796          * Fires when a panel is added. 
37797          * @param {Roo.LayoutRegion} this
37798          * @param {Roo.ContentPanel} panel The panel
37799          */
37800         "paneladded" : true,
37801         /**
37802          * @event panelremoved
37803          * Fires when a panel is removed. 
37804          * @param {Roo.LayoutRegion} this
37805          * @param {Roo.ContentPanel} panel The panel
37806          */
37807         "panelremoved" : true,
37808         /**
37809          * @event beforecollapse
37810          * Fires when this region before collapse.
37811          * @param {Roo.LayoutRegion} this
37812          */
37813         "beforecollapse" : true,
37814         /**
37815          * @event collapsed
37816          * Fires when this region is collapsed.
37817          * @param {Roo.LayoutRegion} this
37818          */
37819         "collapsed" : true,
37820         /**
37821          * @event expanded
37822          * Fires when this region is expanded.
37823          * @param {Roo.LayoutRegion} this
37824          */
37825         "expanded" : true,
37826         /**
37827          * @event slideshow
37828          * Fires when this region is slid into view.
37829          * @param {Roo.LayoutRegion} this
37830          */
37831         "slideshow" : true,
37832         /**
37833          * @event slidehide
37834          * Fires when this region slides out of view. 
37835          * @param {Roo.LayoutRegion} this
37836          */
37837         "slidehide" : true,
37838         /**
37839          * @event panelactivated
37840          * Fires when a panel is activated. 
37841          * @param {Roo.LayoutRegion} this
37842          * @param {Roo.ContentPanel} panel The activated panel
37843          */
37844         "panelactivated" : true,
37845         /**
37846          * @event resized
37847          * Fires when the user resizes this region. 
37848          * @param {Roo.LayoutRegion} this
37849          * @param {Number} newSize The new size (width for east/west, height for north/south)
37850          */
37851         "resized" : true
37852     };
37853     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37854     this.panels = new Roo.util.MixedCollection();
37855     this.panels.getKey = this.getPanelId.createDelegate(this);
37856     this.box = null;
37857     this.activePanel = null;
37858     // ensure listeners are added...
37859     
37860     if (config.listeners || config.events) {
37861         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37862             listeners : config.listeners || {},
37863             events : config.events || {}
37864         });
37865     }
37866     
37867     if(skipConfig !== true){
37868         this.applyConfig(config);
37869     }
37870 };
37871
37872 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37873 {
37874     getPanelId : function(p){
37875         return p.getId();
37876     },
37877     
37878     applyConfig : function(config){
37879         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37880         this.config = config;
37881         
37882     },
37883     
37884     /**
37885      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37886      * the width, for horizontal (north, south) the height.
37887      * @param {Number} newSize The new width or height
37888      */
37889     resizeTo : function(newSize){
37890         var el = this.el ? this.el :
37891                  (this.activePanel ? this.activePanel.getEl() : null);
37892         if(el){
37893             switch(this.position){
37894                 case "east":
37895                 case "west":
37896                     el.setWidth(newSize);
37897                     this.fireEvent("resized", this, newSize);
37898                 break;
37899                 case "north":
37900                 case "south":
37901                     el.setHeight(newSize);
37902                     this.fireEvent("resized", this, newSize);
37903                 break;                
37904             }
37905         }
37906     },
37907     
37908     getBox : function(){
37909         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37910     },
37911     
37912     getMargins : function(){
37913         return this.margins;
37914     },
37915     
37916     updateBox : function(box){
37917         this.box = box;
37918         var el = this.activePanel.getEl();
37919         el.dom.style.left = box.x + "px";
37920         el.dom.style.top = box.y + "px";
37921         this.activePanel.setSize(box.width, box.height);
37922     },
37923     
37924     /**
37925      * Returns the container element for this region.
37926      * @return {Roo.Element}
37927      */
37928     getEl : function(){
37929         return this.activePanel;
37930     },
37931     
37932     /**
37933      * Returns true if this region is currently visible.
37934      * @return {Boolean}
37935      */
37936     isVisible : function(){
37937         return this.activePanel ? true : false;
37938     },
37939     
37940     setActivePanel : function(panel){
37941         panel = this.getPanel(panel);
37942         if(this.activePanel && this.activePanel != panel){
37943             this.activePanel.setActiveState(false);
37944             this.activePanel.getEl().setLeftTop(-10000,-10000);
37945         }
37946         this.activePanel = panel;
37947         panel.setActiveState(true);
37948         if(this.box){
37949             panel.setSize(this.box.width, this.box.height);
37950         }
37951         this.fireEvent("panelactivated", this, panel);
37952         this.fireEvent("invalidated");
37953     },
37954     
37955     /**
37956      * Show the specified panel.
37957      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37958      * @return {Roo.ContentPanel} The shown panel or null
37959      */
37960     showPanel : function(panel){
37961         panel = this.getPanel(panel);
37962         if(panel){
37963             this.setActivePanel(panel);
37964         }
37965         return panel;
37966     },
37967     
37968     /**
37969      * Get the active panel for this region.
37970      * @return {Roo.ContentPanel} The active panel or null
37971      */
37972     getActivePanel : function(){
37973         return this.activePanel;
37974     },
37975     
37976     /**
37977      * Add the passed ContentPanel(s)
37978      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37979      * @return {Roo.ContentPanel} The panel added (if only one was added)
37980      */
37981     add : function(panel){
37982         if(arguments.length > 1){
37983             for(var i = 0, len = arguments.length; i < len; i++) {
37984                 this.add(arguments[i]);
37985             }
37986             return null;
37987         }
37988         if(this.hasPanel(panel)){
37989             this.showPanel(panel);
37990             return panel;
37991         }
37992         var el = panel.getEl();
37993         if(el.dom.parentNode != this.mgr.el.dom){
37994             this.mgr.el.dom.appendChild(el.dom);
37995         }
37996         if(panel.setRegion){
37997             panel.setRegion(this);
37998         }
37999         this.panels.add(panel);
38000         el.setStyle("position", "absolute");
38001         if(!panel.background){
38002             this.setActivePanel(panel);
38003             if(this.config.initialSize && this.panels.getCount()==1){
38004                 this.resizeTo(this.config.initialSize);
38005             }
38006         }
38007         this.fireEvent("paneladded", this, panel);
38008         return panel;
38009     },
38010     
38011     /**
38012      * Returns true if the panel is in this region.
38013      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38014      * @return {Boolean}
38015      */
38016     hasPanel : function(panel){
38017         if(typeof panel == "object"){ // must be panel obj
38018             panel = panel.getId();
38019         }
38020         return this.getPanel(panel) ? true : false;
38021     },
38022     
38023     /**
38024      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38025      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38026      * @param {Boolean} preservePanel Overrides the config preservePanel option
38027      * @return {Roo.ContentPanel} The panel that was removed
38028      */
38029     remove : function(panel, preservePanel){
38030         panel = this.getPanel(panel);
38031         if(!panel){
38032             return null;
38033         }
38034         var e = {};
38035         this.fireEvent("beforeremove", this, panel, e);
38036         if(e.cancel === true){
38037             return null;
38038         }
38039         var panelId = panel.getId();
38040         this.panels.removeKey(panelId);
38041         return panel;
38042     },
38043     
38044     /**
38045      * Returns the panel specified or null if it's not in this region.
38046      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38047      * @return {Roo.ContentPanel}
38048      */
38049     getPanel : function(id){
38050         if(typeof id == "object"){ // must be panel obj
38051             return id;
38052         }
38053         return this.panels.get(id);
38054     },
38055     
38056     /**
38057      * Returns this regions position (north/south/east/west/center).
38058      * @return {String} 
38059      */
38060     getPosition: function(){
38061         return this.position;    
38062     }
38063 });/*
38064  * Based on:
38065  * Ext JS Library 1.1.1
38066  * Copyright(c) 2006-2007, Ext JS, LLC.
38067  *
38068  * Originally Released Under LGPL - original licence link has changed is not relivant.
38069  *
38070  * Fork - LGPL
38071  * <script type="text/javascript">
38072  */
38073  
38074 /**
38075  * @class Roo.bootstrap.layout.Region
38076  * @extends Roo.bootstrap.layout.Basic
38077  * This class represents a region in a layout manager.
38078  
38079  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38080  * @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})
38081  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38082  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38083  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38084  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38085  * @cfg {String}    title           The title for the region (overrides panel titles)
38086  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38087  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38088  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38089  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38090  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38091  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38092  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38093  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38094  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38095  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38096
38097  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38098  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38099  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38100  * @cfg {Number}    width           For East/West panels
38101  * @cfg {Number}    height          For North/South panels
38102  * @cfg {Boolean}   split           To show the splitter
38103  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38104  * 
38105  * @cfg {string}   cls             Extra CSS classes to add to region
38106  * 
38107  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38108  * @cfg {string}   region  the region that it inhabits..
38109  *
38110
38111  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38112  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38113
38114  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38115  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38116  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38117  */
38118 Roo.bootstrap.layout.Region = function(config)
38119 {
38120     this.applyConfig(config);
38121
38122     var mgr = config.mgr;
38123     var pos = config.region;
38124     config.skipConfig = true;
38125     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38126     
38127     if (mgr.el) {
38128         this.onRender(mgr.el);   
38129     }
38130      
38131     this.visible = true;
38132     this.collapsed = false;
38133     this.unrendered_panels = [];
38134 };
38135
38136 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38137
38138     position: '', // set by wrapper (eg. north/south etc..)
38139     unrendered_panels : null,  // unrendered panels.
38140     
38141     tabPosition : false,
38142     
38143     mgr: false, // points to 'Border'
38144     
38145     
38146     createBody : function(){
38147         /** This region's body element 
38148         * @type Roo.Element */
38149         this.bodyEl = this.el.createChild({
38150                 tag: "div",
38151                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38152         });
38153     },
38154
38155     onRender: function(ctr, pos)
38156     {
38157         var dh = Roo.DomHelper;
38158         /** This region's container element 
38159         * @type Roo.Element */
38160         this.el = dh.append(ctr.dom, {
38161                 tag: "div",
38162                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38163             }, true);
38164         /** This region's title element 
38165         * @type Roo.Element */
38166     
38167         this.titleEl = dh.append(this.el.dom,  {
38168                 tag: "div",
38169                 unselectable: "on",
38170                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38171                 children:[
38172                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38173                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38174                 ]
38175             }, true);
38176         
38177         this.titleEl.enableDisplayMode();
38178         /** This region's title text element 
38179         * @type HTMLElement */
38180         this.titleTextEl = this.titleEl.dom.firstChild;
38181         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38182         /*
38183         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38184         this.closeBtn.enableDisplayMode();
38185         this.closeBtn.on("click", this.closeClicked, this);
38186         this.closeBtn.hide();
38187     */
38188         this.createBody(this.config);
38189         if(this.config.hideWhenEmpty){
38190             this.hide();
38191             this.on("paneladded", this.validateVisibility, this);
38192             this.on("panelremoved", this.validateVisibility, this);
38193         }
38194         if(this.autoScroll){
38195             this.bodyEl.setStyle("overflow", "auto");
38196         }else{
38197             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38198         }
38199         //if(c.titlebar !== false){
38200             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38201                 this.titleEl.hide();
38202             }else{
38203                 this.titleEl.show();
38204                 if(this.config.title){
38205                     this.titleTextEl.innerHTML = this.config.title;
38206                 }
38207             }
38208         //}
38209         if(this.config.collapsed){
38210             this.collapse(true);
38211         }
38212         if(this.config.hidden){
38213             this.hide();
38214         }
38215         
38216         if (this.unrendered_panels && this.unrendered_panels.length) {
38217             for (var i =0;i< this.unrendered_panels.length; i++) {
38218                 this.add(this.unrendered_panels[i]);
38219             }
38220             this.unrendered_panels = null;
38221             
38222         }
38223         
38224     },
38225     
38226     applyConfig : function(c)
38227     {
38228         /*
38229          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38230             var dh = Roo.DomHelper;
38231             if(c.titlebar !== false){
38232                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38233                 this.collapseBtn.on("click", this.collapse, this);
38234                 this.collapseBtn.enableDisplayMode();
38235                 /*
38236                 if(c.showPin === true || this.showPin){
38237                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38238                     this.stickBtn.enableDisplayMode();
38239                     this.stickBtn.on("click", this.expand, this);
38240                     this.stickBtn.hide();
38241                 }
38242                 
38243             }
38244             */
38245             /** This region's collapsed element
38246             * @type Roo.Element */
38247             /*
38248              *
38249             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38250                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38251             ]}, true);
38252             
38253             if(c.floatable !== false){
38254                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38255                this.collapsedEl.on("click", this.collapseClick, this);
38256             }
38257
38258             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38259                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38260                    id: "message", unselectable: "on", style:{"float":"left"}});
38261                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38262              }
38263             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38264             this.expandBtn.on("click", this.expand, this);
38265             
38266         }
38267         
38268         if(this.collapseBtn){
38269             this.collapseBtn.setVisible(c.collapsible == true);
38270         }
38271         
38272         this.cmargins = c.cmargins || this.cmargins ||
38273                          (this.position == "west" || this.position == "east" ?
38274                              {top: 0, left: 2, right:2, bottom: 0} :
38275                              {top: 2, left: 0, right:0, bottom: 2});
38276         */
38277         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38278         
38279         
38280         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38281         
38282         this.autoScroll = c.autoScroll || false;
38283         
38284         
38285        
38286         
38287         this.duration = c.duration || .30;
38288         this.slideDuration = c.slideDuration || .45;
38289         this.config = c;
38290        
38291     },
38292     /**
38293      * Returns true if this region is currently visible.
38294      * @return {Boolean}
38295      */
38296     isVisible : function(){
38297         return this.visible;
38298     },
38299
38300     /**
38301      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38302      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38303      */
38304     //setCollapsedTitle : function(title){
38305     //    title = title || "&#160;";
38306      //   if(this.collapsedTitleTextEl){
38307       //      this.collapsedTitleTextEl.innerHTML = title;
38308        // }
38309     //},
38310
38311     getBox : function(){
38312         var b;
38313       //  if(!this.collapsed){
38314             b = this.el.getBox(false, true);
38315        // }else{
38316           //  b = this.collapsedEl.getBox(false, true);
38317         //}
38318         return b;
38319     },
38320
38321     getMargins : function(){
38322         return this.margins;
38323         //return this.collapsed ? this.cmargins : this.margins;
38324     },
38325 /*
38326     highlight : function(){
38327         this.el.addClass("x-layout-panel-dragover");
38328     },
38329
38330     unhighlight : function(){
38331         this.el.removeClass("x-layout-panel-dragover");
38332     },
38333 */
38334     updateBox : function(box)
38335     {
38336         if (!this.bodyEl) {
38337             return; // not rendered yet..
38338         }
38339         
38340         this.box = box;
38341         if(!this.collapsed){
38342             this.el.dom.style.left = box.x + "px";
38343             this.el.dom.style.top = box.y + "px";
38344             this.updateBody(box.width, box.height);
38345         }else{
38346             this.collapsedEl.dom.style.left = box.x + "px";
38347             this.collapsedEl.dom.style.top = box.y + "px";
38348             this.collapsedEl.setSize(box.width, box.height);
38349         }
38350         if(this.tabs){
38351             this.tabs.autoSizeTabs();
38352         }
38353     },
38354
38355     updateBody : function(w, h)
38356     {
38357         if(w !== null){
38358             this.el.setWidth(w);
38359             w -= this.el.getBorderWidth("rl");
38360             if(this.config.adjustments){
38361                 w += this.config.adjustments[0];
38362             }
38363         }
38364         if(h !== null && h > 0){
38365             this.el.setHeight(h);
38366             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38367             h -= this.el.getBorderWidth("tb");
38368             if(this.config.adjustments){
38369                 h += this.config.adjustments[1];
38370             }
38371             this.bodyEl.setHeight(h);
38372             if(this.tabs){
38373                 h = this.tabs.syncHeight(h);
38374             }
38375         }
38376         if(this.panelSize){
38377             w = w !== null ? w : this.panelSize.width;
38378             h = h !== null ? h : this.panelSize.height;
38379         }
38380         if(this.activePanel){
38381             var el = this.activePanel.getEl();
38382             w = w !== null ? w : el.getWidth();
38383             h = h !== null ? h : el.getHeight();
38384             this.panelSize = {width: w, height: h};
38385             this.activePanel.setSize(w, h);
38386         }
38387         if(Roo.isIE && this.tabs){
38388             this.tabs.el.repaint();
38389         }
38390     },
38391
38392     /**
38393      * Returns the container element for this region.
38394      * @return {Roo.Element}
38395      */
38396     getEl : function(){
38397         return this.el;
38398     },
38399
38400     /**
38401      * Hides this region.
38402      */
38403     hide : function(){
38404         //if(!this.collapsed){
38405             this.el.dom.style.left = "-2000px";
38406             this.el.hide();
38407         //}else{
38408          //   this.collapsedEl.dom.style.left = "-2000px";
38409          //   this.collapsedEl.hide();
38410        // }
38411         this.visible = false;
38412         this.fireEvent("visibilitychange", this, false);
38413     },
38414
38415     /**
38416      * Shows this region if it was previously hidden.
38417      */
38418     show : function(){
38419         //if(!this.collapsed){
38420             this.el.show();
38421         //}else{
38422         //    this.collapsedEl.show();
38423        // }
38424         this.visible = true;
38425         this.fireEvent("visibilitychange", this, true);
38426     },
38427 /*
38428     closeClicked : function(){
38429         if(this.activePanel){
38430             this.remove(this.activePanel);
38431         }
38432     },
38433
38434     collapseClick : function(e){
38435         if(this.isSlid){
38436            e.stopPropagation();
38437            this.slideIn();
38438         }else{
38439            e.stopPropagation();
38440            this.slideOut();
38441         }
38442     },
38443 */
38444     /**
38445      * Collapses this region.
38446      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38447      */
38448     /*
38449     collapse : function(skipAnim, skipCheck = false){
38450         if(this.collapsed) {
38451             return;
38452         }
38453         
38454         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38455             
38456             this.collapsed = true;
38457             if(this.split){
38458                 this.split.el.hide();
38459             }
38460             if(this.config.animate && skipAnim !== true){
38461                 this.fireEvent("invalidated", this);
38462                 this.animateCollapse();
38463             }else{
38464                 this.el.setLocation(-20000,-20000);
38465                 this.el.hide();
38466                 this.collapsedEl.show();
38467                 this.fireEvent("collapsed", this);
38468                 this.fireEvent("invalidated", this);
38469             }
38470         }
38471         
38472     },
38473 */
38474     animateCollapse : function(){
38475         // overridden
38476     },
38477
38478     /**
38479      * Expands this region if it was previously collapsed.
38480      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38481      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38482      */
38483     /*
38484     expand : function(e, skipAnim){
38485         if(e) {
38486             e.stopPropagation();
38487         }
38488         if(!this.collapsed || this.el.hasActiveFx()) {
38489             return;
38490         }
38491         if(this.isSlid){
38492             this.afterSlideIn();
38493             skipAnim = true;
38494         }
38495         this.collapsed = false;
38496         if(this.config.animate && skipAnim !== true){
38497             this.animateExpand();
38498         }else{
38499             this.el.show();
38500             if(this.split){
38501                 this.split.el.show();
38502             }
38503             this.collapsedEl.setLocation(-2000,-2000);
38504             this.collapsedEl.hide();
38505             this.fireEvent("invalidated", this);
38506             this.fireEvent("expanded", this);
38507         }
38508     },
38509 */
38510     animateExpand : function(){
38511         // overridden
38512     },
38513
38514     initTabs : function()
38515     {
38516         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38517         
38518         var ts = new Roo.bootstrap.panel.Tabs({
38519             el: this.bodyEl.dom,
38520             region : this,
38521             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38522             disableTooltips: this.config.disableTabTips,
38523             toolbar : this.config.toolbar
38524         });
38525         
38526         if(this.config.hideTabs){
38527             ts.stripWrap.setDisplayed(false);
38528         }
38529         this.tabs = ts;
38530         ts.resizeTabs = this.config.resizeTabs === true;
38531         ts.minTabWidth = this.config.minTabWidth || 40;
38532         ts.maxTabWidth = this.config.maxTabWidth || 250;
38533         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38534         ts.monitorResize = false;
38535         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38536         ts.bodyEl.addClass('roo-layout-tabs-body');
38537         this.panels.each(this.initPanelAsTab, this);
38538     },
38539
38540     initPanelAsTab : function(panel){
38541         var ti = this.tabs.addTab(
38542             panel.getEl().id,
38543             panel.getTitle(),
38544             null,
38545             this.config.closeOnTab && panel.isClosable(),
38546             panel.tpl
38547         );
38548         if(panel.tabTip !== undefined){
38549             ti.setTooltip(panel.tabTip);
38550         }
38551         ti.on("activate", function(){
38552               this.setActivePanel(panel);
38553         }, this);
38554         
38555         if(this.config.closeOnTab){
38556             ti.on("beforeclose", function(t, e){
38557                 e.cancel = true;
38558                 this.remove(panel);
38559             }, this);
38560         }
38561         
38562         panel.tabItem = ti;
38563         
38564         return ti;
38565     },
38566
38567     updatePanelTitle : function(panel, title)
38568     {
38569         if(this.activePanel == panel){
38570             this.updateTitle(title);
38571         }
38572         if(this.tabs){
38573             var ti = this.tabs.getTab(panel.getEl().id);
38574             ti.setText(title);
38575             if(panel.tabTip !== undefined){
38576                 ti.setTooltip(panel.tabTip);
38577             }
38578         }
38579     },
38580
38581     updateTitle : function(title){
38582         if(this.titleTextEl && !this.config.title){
38583             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38584         }
38585     },
38586
38587     setActivePanel : function(panel)
38588     {
38589         panel = this.getPanel(panel);
38590         if(this.activePanel && this.activePanel != panel){
38591             if(this.activePanel.setActiveState(false) === false){
38592                 return;
38593             }
38594         }
38595         this.activePanel = panel;
38596         panel.setActiveState(true);
38597         if(this.panelSize){
38598             panel.setSize(this.panelSize.width, this.panelSize.height);
38599         }
38600         if(this.closeBtn){
38601             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38602         }
38603         this.updateTitle(panel.getTitle());
38604         if(this.tabs){
38605             this.fireEvent("invalidated", this);
38606         }
38607         this.fireEvent("panelactivated", this, panel);
38608     },
38609
38610     /**
38611      * Shows the specified panel.
38612      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38613      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38614      */
38615     showPanel : function(panel)
38616     {
38617         panel = this.getPanel(panel);
38618         if(panel){
38619             if(this.tabs){
38620                 var tab = this.tabs.getTab(panel.getEl().id);
38621                 if(tab.isHidden()){
38622                     this.tabs.unhideTab(tab.id);
38623                 }
38624                 tab.activate();
38625             }else{
38626                 this.setActivePanel(panel);
38627             }
38628         }
38629         return panel;
38630     },
38631
38632     /**
38633      * Get the active panel for this region.
38634      * @return {Roo.ContentPanel} The active panel or null
38635      */
38636     getActivePanel : function(){
38637         return this.activePanel;
38638     },
38639
38640     validateVisibility : function(){
38641         if(this.panels.getCount() < 1){
38642             this.updateTitle("&#160;");
38643             this.closeBtn.hide();
38644             this.hide();
38645         }else{
38646             if(!this.isVisible()){
38647                 this.show();
38648             }
38649         }
38650     },
38651
38652     /**
38653      * Adds the passed ContentPanel(s) to this region.
38654      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38655      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38656      */
38657     add : function(panel)
38658     {
38659         if(arguments.length > 1){
38660             for(var i = 0, len = arguments.length; i < len; i++) {
38661                 this.add(arguments[i]);
38662             }
38663             return null;
38664         }
38665         
38666         // if we have not been rendered yet, then we can not really do much of this..
38667         if (!this.bodyEl) {
38668             this.unrendered_panels.push(panel);
38669             return panel;
38670         }
38671         
38672         
38673         
38674         
38675         if(this.hasPanel(panel)){
38676             this.showPanel(panel);
38677             return panel;
38678         }
38679         panel.setRegion(this);
38680         this.panels.add(panel);
38681        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38682             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38683             // and hide them... ???
38684             this.bodyEl.dom.appendChild(panel.getEl().dom);
38685             if(panel.background !== true){
38686                 this.setActivePanel(panel);
38687             }
38688             this.fireEvent("paneladded", this, panel);
38689             return panel;
38690         }
38691         */
38692         if(!this.tabs){
38693             this.initTabs();
38694         }else{
38695             this.initPanelAsTab(panel);
38696         }
38697         
38698         
38699         if(panel.background !== true){
38700             this.tabs.activate(panel.getEl().id);
38701         }
38702         this.fireEvent("paneladded", this, panel);
38703         return panel;
38704     },
38705
38706     /**
38707      * Hides the tab for the specified panel.
38708      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38709      */
38710     hidePanel : function(panel){
38711         if(this.tabs && (panel = this.getPanel(panel))){
38712             this.tabs.hideTab(panel.getEl().id);
38713         }
38714     },
38715
38716     /**
38717      * Unhides the tab for a previously hidden panel.
38718      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38719      */
38720     unhidePanel : function(panel){
38721         if(this.tabs && (panel = this.getPanel(panel))){
38722             this.tabs.unhideTab(panel.getEl().id);
38723         }
38724     },
38725
38726     clearPanels : function(){
38727         while(this.panels.getCount() > 0){
38728              this.remove(this.panels.first());
38729         }
38730     },
38731
38732     /**
38733      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38734      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38735      * @param {Boolean} preservePanel Overrides the config preservePanel option
38736      * @return {Roo.ContentPanel} The panel that was removed
38737      */
38738     remove : function(panel, preservePanel)
38739     {
38740         panel = this.getPanel(panel);
38741         if(!panel){
38742             return null;
38743         }
38744         var e = {};
38745         this.fireEvent("beforeremove", this, panel, e);
38746         if(e.cancel === true){
38747             return null;
38748         }
38749         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38750         var panelId = panel.getId();
38751         this.panels.removeKey(panelId);
38752         if(preservePanel){
38753             document.body.appendChild(panel.getEl().dom);
38754         }
38755         if(this.tabs){
38756             this.tabs.removeTab(panel.getEl().id);
38757         }else if (!preservePanel){
38758             this.bodyEl.dom.removeChild(panel.getEl().dom);
38759         }
38760         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38761             var p = this.panels.first();
38762             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38763             tempEl.appendChild(p.getEl().dom);
38764             this.bodyEl.update("");
38765             this.bodyEl.dom.appendChild(p.getEl().dom);
38766             tempEl = null;
38767             this.updateTitle(p.getTitle());
38768             this.tabs = null;
38769             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38770             this.setActivePanel(p);
38771         }
38772         panel.setRegion(null);
38773         if(this.activePanel == panel){
38774             this.activePanel = null;
38775         }
38776         if(this.config.autoDestroy !== false && preservePanel !== true){
38777             try{panel.destroy();}catch(e){}
38778         }
38779         this.fireEvent("panelremoved", this, panel);
38780         return panel;
38781     },
38782
38783     /**
38784      * Returns the TabPanel component used by this region
38785      * @return {Roo.TabPanel}
38786      */
38787     getTabs : function(){
38788         return this.tabs;
38789     },
38790
38791     createTool : function(parentEl, className){
38792         var btn = Roo.DomHelper.append(parentEl, {
38793             tag: "div",
38794             cls: "x-layout-tools-button",
38795             children: [ {
38796                 tag: "div",
38797                 cls: "roo-layout-tools-button-inner " + className,
38798                 html: "&#160;"
38799             }]
38800         }, true);
38801         btn.addClassOnOver("roo-layout-tools-button-over");
38802         return btn;
38803     }
38804 });/*
38805  * Based on:
38806  * Ext JS Library 1.1.1
38807  * Copyright(c) 2006-2007, Ext JS, LLC.
38808  *
38809  * Originally Released Under LGPL - original licence link has changed is not relivant.
38810  *
38811  * Fork - LGPL
38812  * <script type="text/javascript">
38813  */
38814  
38815
38816
38817 /**
38818  * @class Roo.SplitLayoutRegion
38819  * @extends Roo.LayoutRegion
38820  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38821  */
38822 Roo.bootstrap.layout.Split = function(config){
38823     this.cursor = config.cursor;
38824     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38825 };
38826
38827 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38828 {
38829     splitTip : "Drag to resize.",
38830     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38831     useSplitTips : false,
38832
38833     applyConfig : function(config){
38834         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38835     },
38836     
38837     onRender : function(ctr,pos) {
38838         
38839         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38840         if(!this.config.split){
38841             return;
38842         }
38843         if(!this.split){
38844             
38845             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38846                             tag: "div",
38847                             id: this.el.id + "-split",
38848                             cls: "roo-layout-split roo-layout-split-"+this.position,
38849                             html: "&#160;"
38850             });
38851             /** The SplitBar for this region 
38852             * @type Roo.SplitBar */
38853             // does not exist yet...
38854             Roo.log([this.position, this.orientation]);
38855             
38856             this.split = new Roo.bootstrap.SplitBar({
38857                 dragElement : splitEl,
38858                 resizingElement: this.el,
38859                 orientation : this.orientation
38860             });
38861             
38862             this.split.on("moved", this.onSplitMove, this);
38863             this.split.useShim = this.config.useShim === true;
38864             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38865             if(this.useSplitTips){
38866                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38867             }
38868             //if(config.collapsible){
38869             //    this.split.el.on("dblclick", this.collapse,  this);
38870             //}
38871         }
38872         if(typeof this.config.minSize != "undefined"){
38873             this.split.minSize = this.config.minSize;
38874         }
38875         if(typeof this.config.maxSize != "undefined"){
38876             this.split.maxSize = this.config.maxSize;
38877         }
38878         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38879             this.hideSplitter();
38880         }
38881         
38882     },
38883
38884     getHMaxSize : function(){
38885          var cmax = this.config.maxSize || 10000;
38886          var center = this.mgr.getRegion("center");
38887          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38888     },
38889
38890     getVMaxSize : function(){
38891          var cmax = this.config.maxSize || 10000;
38892          var center = this.mgr.getRegion("center");
38893          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38894     },
38895
38896     onSplitMove : function(split, newSize){
38897         this.fireEvent("resized", this, newSize);
38898     },
38899     
38900     /** 
38901      * Returns the {@link Roo.SplitBar} for this region.
38902      * @return {Roo.SplitBar}
38903      */
38904     getSplitBar : function(){
38905         return this.split;
38906     },
38907     
38908     hide : function(){
38909         this.hideSplitter();
38910         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38911     },
38912
38913     hideSplitter : function(){
38914         if(this.split){
38915             this.split.el.setLocation(-2000,-2000);
38916             this.split.el.hide();
38917         }
38918     },
38919
38920     show : function(){
38921         if(this.split){
38922             this.split.el.show();
38923         }
38924         Roo.bootstrap.layout.Split.superclass.show.call(this);
38925     },
38926     
38927     beforeSlide: function(){
38928         if(Roo.isGecko){// firefox overflow auto bug workaround
38929             this.bodyEl.clip();
38930             if(this.tabs) {
38931                 this.tabs.bodyEl.clip();
38932             }
38933             if(this.activePanel){
38934                 this.activePanel.getEl().clip();
38935                 
38936                 if(this.activePanel.beforeSlide){
38937                     this.activePanel.beforeSlide();
38938                 }
38939             }
38940         }
38941     },
38942     
38943     afterSlide : function(){
38944         if(Roo.isGecko){// firefox overflow auto bug workaround
38945             this.bodyEl.unclip();
38946             if(this.tabs) {
38947                 this.tabs.bodyEl.unclip();
38948             }
38949             if(this.activePanel){
38950                 this.activePanel.getEl().unclip();
38951                 if(this.activePanel.afterSlide){
38952                     this.activePanel.afterSlide();
38953                 }
38954             }
38955         }
38956     },
38957
38958     initAutoHide : function(){
38959         if(this.autoHide !== false){
38960             if(!this.autoHideHd){
38961                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38962                 this.autoHideHd = {
38963                     "mouseout": function(e){
38964                         if(!e.within(this.el, true)){
38965                             st.delay(500);
38966                         }
38967                     },
38968                     "mouseover" : function(e){
38969                         st.cancel();
38970                     },
38971                     scope : this
38972                 };
38973             }
38974             this.el.on(this.autoHideHd);
38975         }
38976     },
38977
38978     clearAutoHide : function(){
38979         if(this.autoHide !== false){
38980             this.el.un("mouseout", this.autoHideHd.mouseout);
38981             this.el.un("mouseover", this.autoHideHd.mouseover);
38982         }
38983     },
38984
38985     clearMonitor : function(){
38986         Roo.get(document).un("click", this.slideInIf, this);
38987     },
38988
38989     // these names are backwards but not changed for compat
38990     slideOut : function(){
38991         if(this.isSlid || this.el.hasActiveFx()){
38992             return;
38993         }
38994         this.isSlid = true;
38995         if(this.collapseBtn){
38996             this.collapseBtn.hide();
38997         }
38998         this.closeBtnState = this.closeBtn.getStyle('display');
38999         this.closeBtn.hide();
39000         if(this.stickBtn){
39001             this.stickBtn.show();
39002         }
39003         this.el.show();
39004         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39005         this.beforeSlide();
39006         this.el.setStyle("z-index", 10001);
39007         this.el.slideIn(this.getSlideAnchor(), {
39008             callback: function(){
39009                 this.afterSlide();
39010                 this.initAutoHide();
39011                 Roo.get(document).on("click", this.slideInIf, this);
39012                 this.fireEvent("slideshow", this);
39013             },
39014             scope: this,
39015             block: true
39016         });
39017     },
39018
39019     afterSlideIn : function(){
39020         this.clearAutoHide();
39021         this.isSlid = false;
39022         this.clearMonitor();
39023         this.el.setStyle("z-index", "");
39024         if(this.collapseBtn){
39025             this.collapseBtn.show();
39026         }
39027         this.closeBtn.setStyle('display', this.closeBtnState);
39028         if(this.stickBtn){
39029             this.stickBtn.hide();
39030         }
39031         this.fireEvent("slidehide", this);
39032     },
39033
39034     slideIn : function(cb){
39035         if(!this.isSlid || this.el.hasActiveFx()){
39036             Roo.callback(cb);
39037             return;
39038         }
39039         this.isSlid = false;
39040         this.beforeSlide();
39041         this.el.slideOut(this.getSlideAnchor(), {
39042             callback: function(){
39043                 this.el.setLeftTop(-10000, -10000);
39044                 this.afterSlide();
39045                 this.afterSlideIn();
39046                 Roo.callback(cb);
39047             },
39048             scope: this,
39049             block: true
39050         });
39051     },
39052     
39053     slideInIf : function(e){
39054         if(!e.within(this.el)){
39055             this.slideIn();
39056         }
39057     },
39058
39059     animateCollapse : function(){
39060         this.beforeSlide();
39061         this.el.setStyle("z-index", 20000);
39062         var anchor = this.getSlideAnchor();
39063         this.el.slideOut(anchor, {
39064             callback : function(){
39065                 this.el.setStyle("z-index", "");
39066                 this.collapsedEl.slideIn(anchor, {duration:.3});
39067                 this.afterSlide();
39068                 this.el.setLocation(-10000,-10000);
39069                 this.el.hide();
39070                 this.fireEvent("collapsed", this);
39071             },
39072             scope: this,
39073             block: true
39074         });
39075     },
39076
39077     animateExpand : function(){
39078         this.beforeSlide();
39079         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39080         this.el.setStyle("z-index", 20000);
39081         this.collapsedEl.hide({
39082             duration:.1
39083         });
39084         this.el.slideIn(this.getSlideAnchor(), {
39085             callback : function(){
39086                 this.el.setStyle("z-index", "");
39087                 this.afterSlide();
39088                 if(this.split){
39089                     this.split.el.show();
39090                 }
39091                 this.fireEvent("invalidated", this);
39092                 this.fireEvent("expanded", this);
39093             },
39094             scope: this,
39095             block: true
39096         });
39097     },
39098
39099     anchors : {
39100         "west" : "left",
39101         "east" : "right",
39102         "north" : "top",
39103         "south" : "bottom"
39104     },
39105
39106     sanchors : {
39107         "west" : "l",
39108         "east" : "r",
39109         "north" : "t",
39110         "south" : "b"
39111     },
39112
39113     canchors : {
39114         "west" : "tl-tr",
39115         "east" : "tr-tl",
39116         "north" : "tl-bl",
39117         "south" : "bl-tl"
39118     },
39119
39120     getAnchor : function(){
39121         return this.anchors[this.position];
39122     },
39123
39124     getCollapseAnchor : function(){
39125         return this.canchors[this.position];
39126     },
39127
39128     getSlideAnchor : function(){
39129         return this.sanchors[this.position];
39130     },
39131
39132     getAlignAdj : function(){
39133         var cm = this.cmargins;
39134         switch(this.position){
39135             case "west":
39136                 return [0, 0];
39137             break;
39138             case "east":
39139                 return [0, 0];
39140             break;
39141             case "north":
39142                 return [0, 0];
39143             break;
39144             case "south":
39145                 return [0, 0];
39146             break;
39147         }
39148     },
39149
39150     getExpandAdj : function(){
39151         var c = this.collapsedEl, cm = this.cmargins;
39152         switch(this.position){
39153             case "west":
39154                 return [-(cm.right+c.getWidth()+cm.left), 0];
39155             break;
39156             case "east":
39157                 return [cm.right+c.getWidth()+cm.left, 0];
39158             break;
39159             case "north":
39160                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39161             break;
39162             case "south":
39163                 return [0, cm.top+cm.bottom+c.getHeight()];
39164             break;
39165         }
39166     }
39167 });/*
39168  * Based on:
39169  * Ext JS Library 1.1.1
39170  * Copyright(c) 2006-2007, Ext JS, LLC.
39171  *
39172  * Originally Released Under LGPL - original licence link has changed is not relivant.
39173  *
39174  * Fork - LGPL
39175  * <script type="text/javascript">
39176  */
39177 /*
39178  * These classes are private internal classes
39179  */
39180 Roo.bootstrap.layout.Center = function(config){
39181     config.region = "center";
39182     Roo.bootstrap.layout.Region.call(this, config);
39183     this.visible = true;
39184     this.minWidth = config.minWidth || 20;
39185     this.minHeight = config.minHeight || 20;
39186 };
39187
39188 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39189     hide : function(){
39190         // center panel can't be hidden
39191     },
39192     
39193     show : function(){
39194         // center panel can't be hidden
39195     },
39196     
39197     getMinWidth: function(){
39198         return this.minWidth;
39199     },
39200     
39201     getMinHeight: function(){
39202         return this.minHeight;
39203     }
39204 });
39205
39206
39207
39208
39209  
39210
39211
39212
39213
39214
39215
39216 Roo.bootstrap.layout.North = function(config)
39217 {
39218     config.region = 'north';
39219     config.cursor = 'n-resize';
39220     
39221     Roo.bootstrap.layout.Split.call(this, config);
39222     
39223     
39224     if(this.split){
39225         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39226         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39227         this.split.el.addClass("roo-layout-split-v");
39228     }
39229     var size = config.initialSize || config.height;
39230     if(typeof size != "undefined"){
39231         this.el.setHeight(size);
39232     }
39233 };
39234 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39235 {
39236     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39237     
39238     
39239     
39240     getBox : function(){
39241         if(this.collapsed){
39242             return this.collapsedEl.getBox();
39243         }
39244         var box = this.el.getBox();
39245         if(this.split){
39246             box.height += this.split.el.getHeight();
39247         }
39248         return box;
39249     },
39250     
39251     updateBox : function(box){
39252         if(this.split && !this.collapsed){
39253             box.height -= this.split.el.getHeight();
39254             this.split.el.setLeft(box.x);
39255             this.split.el.setTop(box.y+box.height);
39256             this.split.el.setWidth(box.width);
39257         }
39258         if(this.collapsed){
39259             this.updateBody(box.width, null);
39260         }
39261         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39262     }
39263 });
39264
39265
39266
39267
39268
39269 Roo.bootstrap.layout.South = function(config){
39270     config.region = 'south';
39271     config.cursor = 's-resize';
39272     Roo.bootstrap.layout.Split.call(this, config);
39273     if(this.split){
39274         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39275         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39276         this.split.el.addClass("roo-layout-split-v");
39277     }
39278     var size = config.initialSize || config.height;
39279     if(typeof size != "undefined"){
39280         this.el.setHeight(size);
39281     }
39282 };
39283
39284 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39285     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39286     getBox : function(){
39287         if(this.collapsed){
39288             return this.collapsedEl.getBox();
39289         }
39290         var box = this.el.getBox();
39291         if(this.split){
39292             var sh = this.split.el.getHeight();
39293             box.height += sh;
39294             box.y -= sh;
39295         }
39296         return box;
39297     },
39298     
39299     updateBox : function(box){
39300         if(this.split && !this.collapsed){
39301             var sh = this.split.el.getHeight();
39302             box.height -= sh;
39303             box.y += sh;
39304             this.split.el.setLeft(box.x);
39305             this.split.el.setTop(box.y-sh);
39306             this.split.el.setWidth(box.width);
39307         }
39308         if(this.collapsed){
39309             this.updateBody(box.width, null);
39310         }
39311         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39312     }
39313 });
39314
39315 Roo.bootstrap.layout.East = function(config){
39316     config.region = "east";
39317     config.cursor = "e-resize";
39318     Roo.bootstrap.layout.Split.call(this, config);
39319     if(this.split){
39320         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39321         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39322         this.split.el.addClass("roo-layout-split-h");
39323     }
39324     var size = config.initialSize || config.width;
39325     if(typeof size != "undefined"){
39326         this.el.setWidth(size);
39327     }
39328 };
39329 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39330     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39331     getBox : function(){
39332         if(this.collapsed){
39333             return this.collapsedEl.getBox();
39334         }
39335         var box = this.el.getBox();
39336         if(this.split){
39337             var sw = this.split.el.getWidth();
39338             box.width += sw;
39339             box.x -= sw;
39340         }
39341         return box;
39342     },
39343
39344     updateBox : function(box){
39345         if(this.split && !this.collapsed){
39346             var sw = this.split.el.getWidth();
39347             box.width -= sw;
39348             this.split.el.setLeft(box.x);
39349             this.split.el.setTop(box.y);
39350             this.split.el.setHeight(box.height);
39351             box.x += sw;
39352         }
39353         if(this.collapsed){
39354             this.updateBody(null, box.height);
39355         }
39356         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39357     }
39358 });
39359
39360 Roo.bootstrap.layout.West = function(config){
39361     config.region = "west";
39362     config.cursor = "w-resize";
39363     
39364     Roo.bootstrap.layout.Split.call(this, config);
39365     if(this.split){
39366         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39367         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39368         this.split.el.addClass("roo-layout-split-h");
39369     }
39370     
39371 };
39372 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39373     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39374     
39375     onRender: function(ctr, pos)
39376     {
39377         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39378         var size = this.config.initialSize || this.config.width;
39379         if(typeof size != "undefined"){
39380             this.el.setWidth(size);
39381         }
39382     },
39383     
39384     getBox : function(){
39385         if(this.collapsed){
39386             return this.collapsedEl.getBox();
39387         }
39388         var box = this.el.getBox();
39389         if(this.split){
39390             box.width += this.split.el.getWidth();
39391         }
39392         return box;
39393     },
39394     
39395     updateBox : function(box){
39396         if(this.split && !this.collapsed){
39397             var sw = this.split.el.getWidth();
39398             box.width -= sw;
39399             this.split.el.setLeft(box.x+box.width);
39400             this.split.el.setTop(box.y);
39401             this.split.el.setHeight(box.height);
39402         }
39403         if(this.collapsed){
39404             this.updateBody(null, box.height);
39405         }
39406         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39407     }
39408 });Roo.namespace("Roo.bootstrap.panel");/*
39409  * Based on:
39410  * Ext JS Library 1.1.1
39411  * Copyright(c) 2006-2007, Ext JS, LLC.
39412  *
39413  * Originally Released Under LGPL - original licence link has changed is not relivant.
39414  *
39415  * Fork - LGPL
39416  * <script type="text/javascript">
39417  */
39418 /**
39419  * @class Roo.ContentPanel
39420  * @extends Roo.util.Observable
39421  * A basic ContentPanel element.
39422  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39423  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39424  * @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
39425  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39426  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39427  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39428  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39429  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39430  * @cfg {String} title          The title for this panel
39431  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39432  * @cfg {String} url            Calls {@link #setUrl} with this value
39433  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39434  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39435  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39436  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39437  * @cfg {Boolean} badges render the badges
39438  * @cfg {String} cls  extra classes to use  
39439  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39440
39441  * @constructor
39442  * Create a new ContentPanel.
39443  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39444  * @param {String/Object} config A string to set only the title or a config object
39445  * @param {String} content (optional) Set the HTML content for this panel
39446  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39447  */
39448 Roo.bootstrap.panel.Content = function( config){
39449     
39450     this.tpl = config.tpl || false;
39451     
39452     var el = config.el;
39453     var content = config.content;
39454
39455     if(config.autoCreate){ // xtype is available if this is called from factory
39456         el = Roo.id();
39457     }
39458     this.el = Roo.get(el);
39459     if(!this.el && config && config.autoCreate){
39460         if(typeof config.autoCreate == "object"){
39461             if(!config.autoCreate.id){
39462                 config.autoCreate.id = config.id||el;
39463             }
39464             this.el = Roo.DomHelper.append(document.body,
39465                         config.autoCreate, true);
39466         }else{
39467             var elcfg =  {
39468                 tag: "div",
39469                 cls: (config.cls || '') +
39470                     (config.background ? ' bg-' + config.background : '') +
39471                     " roo-layout-inactive-content",
39472                 id: config.id||el
39473             };
39474             if (config.html) {
39475                 elcfg.html = config.html;
39476                 
39477             }
39478                         
39479             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39480         }
39481     } 
39482     this.closable = false;
39483     this.loaded = false;
39484     this.active = false;
39485    
39486       
39487     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39488         
39489         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39490         
39491         this.wrapEl = this.el; //this.el.wrap();
39492         var ti = [];
39493         if (config.toolbar.items) {
39494             ti = config.toolbar.items ;
39495             delete config.toolbar.items ;
39496         }
39497         
39498         var nitems = [];
39499         this.toolbar.render(this.wrapEl, 'before');
39500         for(var i =0;i < ti.length;i++) {
39501           //  Roo.log(['add child', items[i]]);
39502             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39503         }
39504         this.toolbar.items = nitems;
39505         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39506         delete config.toolbar;
39507         
39508     }
39509     /*
39510     // xtype created footer. - not sure if will work as we normally have to render first..
39511     if (this.footer && !this.footer.el && this.footer.xtype) {
39512         if (!this.wrapEl) {
39513             this.wrapEl = this.el.wrap();
39514         }
39515     
39516         this.footer.container = this.wrapEl.createChild();
39517          
39518         this.footer = Roo.factory(this.footer, Roo);
39519         
39520     }
39521     */
39522     
39523      if(typeof config == "string"){
39524         this.title = config;
39525     }else{
39526         Roo.apply(this, config);
39527     }
39528     
39529     if(this.resizeEl){
39530         this.resizeEl = Roo.get(this.resizeEl, true);
39531     }else{
39532         this.resizeEl = this.el;
39533     }
39534     // handle view.xtype
39535     
39536  
39537     
39538     
39539     this.addEvents({
39540         /**
39541          * @event activate
39542          * Fires when this panel is activated. 
39543          * @param {Roo.ContentPanel} this
39544          */
39545         "activate" : true,
39546         /**
39547          * @event deactivate
39548          * Fires when this panel is activated. 
39549          * @param {Roo.ContentPanel} this
39550          */
39551         "deactivate" : true,
39552
39553         /**
39554          * @event resize
39555          * Fires when this panel is resized if fitToFrame is true.
39556          * @param {Roo.ContentPanel} this
39557          * @param {Number} width The width after any component adjustments
39558          * @param {Number} height The height after any component adjustments
39559          */
39560         "resize" : true,
39561         
39562          /**
39563          * @event render
39564          * Fires when this tab is created
39565          * @param {Roo.ContentPanel} this
39566          */
39567         "render" : true
39568         
39569         
39570         
39571     });
39572     
39573
39574     
39575     
39576     if(this.autoScroll){
39577         this.resizeEl.setStyle("overflow", "auto");
39578     } else {
39579         // fix randome scrolling
39580         //this.el.on('scroll', function() {
39581         //    Roo.log('fix random scolling');
39582         //    this.scrollTo('top',0); 
39583         //});
39584     }
39585     content = content || this.content;
39586     if(content){
39587         this.setContent(content);
39588     }
39589     if(config && config.url){
39590         this.setUrl(this.url, this.params, this.loadOnce);
39591     }
39592     
39593     
39594     
39595     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39596     
39597     if (this.view && typeof(this.view.xtype) != 'undefined') {
39598         this.view.el = this.el.appendChild(document.createElement("div"));
39599         this.view = Roo.factory(this.view); 
39600         this.view.render  &&  this.view.render(false, '');  
39601     }
39602     
39603     
39604     this.fireEvent('render', this);
39605 };
39606
39607 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39608     
39609     cls : '',
39610     background : '',
39611     
39612     tabTip : '',
39613     
39614     setRegion : function(region){
39615         this.region = region;
39616         this.setActiveClass(region && !this.background);
39617     },
39618     
39619     
39620     setActiveClass: function(state)
39621     {
39622         if(state){
39623            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39624            this.el.setStyle('position','relative');
39625         }else{
39626            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39627            this.el.setStyle('position', 'absolute');
39628         } 
39629     },
39630     
39631     /**
39632      * Returns the toolbar for this Panel if one was configured. 
39633      * @return {Roo.Toolbar} 
39634      */
39635     getToolbar : function(){
39636         return this.toolbar;
39637     },
39638     
39639     setActiveState : function(active)
39640     {
39641         this.active = active;
39642         this.setActiveClass(active);
39643         if(!active){
39644             if(this.fireEvent("deactivate", this) === false){
39645                 return false;
39646             }
39647             return true;
39648         }
39649         this.fireEvent("activate", this);
39650         return true;
39651     },
39652     /**
39653      * Updates this panel's element
39654      * @param {String} content The new content
39655      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39656     */
39657     setContent : function(content, loadScripts){
39658         this.el.update(content, loadScripts);
39659     },
39660
39661     ignoreResize : function(w, h){
39662         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39663             return true;
39664         }else{
39665             this.lastSize = {width: w, height: h};
39666             return false;
39667         }
39668     },
39669     /**
39670      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39671      * @return {Roo.UpdateManager} The UpdateManager
39672      */
39673     getUpdateManager : function(){
39674         return this.el.getUpdateManager();
39675     },
39676      /**
39677      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39678      * @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:
39679 <pre><code>
39680 panel.load({
39681     url: "your-url.php",
39682     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39683     callback: yourFunction,
39684     scope: yourObject, //(optional scope)
39685     discardUrl: false,
39686     nocache: false,
39687     text: "Loading...",
39688     timeout: 30,
39689     scripts: false
39690 });
39691 </code></pre>
39692      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39693      * 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.
39694      * @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}
39695      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39696      * @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.
39697      * @return {Roo.ContentPanel} this
39698      */
39699     load : function(){
39700         var um = this.el.getUpdateManager();
39701         um.update.apply(um, arguments);
39702         return this;
39703     },
39704
39705
39706     /**
39707      * 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.
39708      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39709      * @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)
39710      * @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)
39711      * @return {Roo.UpdateManager} The UpdateManager
39712      */
39713     setUrl : function(url, params, loadOnce){
39714         if(this.refreshDelegate){
39715             this.removeListener("activate", this.refreshDelegate);
39716         }
39717         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39718         this.on("activate", this.refreshDelegate);
39719         return this.el.getUpdateManager();
39720     },
39721     
39722     _handleRefresh : function(url, params, loadOnce){
39723         if(!loadOnce || !this.loaded){
39724             var updater = this.el.getUpdateManager();
39725             updater.update(url, params, this._setLoaded.createDelegate(this));
39726         }
39727     },
39728     
39729     _setLoaded : function(){
39730         this.loaded = true;
39731     }, 
39732     
39733     /**
39734      * Returns this panel's id
39735      * @return {String} 
39736      */
39737     getId : function(){
39738         return this.el.id;
39739     },
39740     
39741     /** 
39742      * Returns this panel's element - used by regiosn to add.
39743      * @return {Roo.Element} 
39744      */
39745     getEl : function(){
39746         return this.wrapEl || this.el;
39747     },
39748     
39749    
39750     
39751     adjustForComponents : function(width, height)
39752     {
39753         //Roo.log('adjustForComponents ');
39754         if(this.resizeEl != this.el){
39755             width -= this.el.getFrameWidth('lr');
39756             height -= this.el.getFrameWidth('tb');
39757         }
39758         if(this.toolbar){
39759             var te = this.toolbar.getEl();
39760             te.setWidth(width);
39761             height -= te.getHeight();
39762         }
39763         if(this.footer){
39764             var te = this.footer.getEl();
39765             te.setWidth(width);
39766             height -= te.getHeight();
39767         }
39768         
39769         
39770         if(this.adjustments){
39771             width += this.adjustments[0];
39772             height += this.adjustments[1];
39773         }
39774         return {"width": width, "height": height};
39775     },
39776     
39777     setSize : function(width, height){
39778         if(this.fitToFrame && !this.ignoreResize(width, height)){
39779             if(this.fitContainer && this.resizeEl != this.el){
39780                 this.el.setSize(width, height);
39781             }
39782             var size = this.adjustForComponents(width, height);
39783             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39784             this.fireEvent('resize', this, size.width, size.height);
39785         }
39786     },
39787     
39788     /**
39789      * Returns this panel's title
39790      * @return {String} 
39791      */
39792     getTitle : function(){
39793         
39794         if (typeof(this.title) != 'object') {
39795             return this.title;
39796         }
39797         
39798         var t = '';
39799         for (var k in this.title) {
39800             if (!this.title.hasOwnProperty(k)) {
39801                 continue;
39802             }
39803             
39804             if (k.indexOf('-') >= 0) {
39805                 var s = k.split('-');
39806                 for (var i = 0; i<s.length; i++) {
39807                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39808                 }
39809             } else {
39810                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39811             }
39812         }
39813         return t;
39814     },
39815     
39816     /**
39817      * Set this panel's title
39818      * @param {String} title
39819      */
39820     setTitle : function(title){
39821         this.title = title;
39822         if(this.region){
39823             this.region.updatePanelTitle(this, title);
39824         }
39825     },
39826     
39827     /**
39828      * Returns true is this panel was configured to be closable
39829      * @return {Boolean} 
39830      */
39831     isClosable : function(){
39832         return this.closable;
39833     },
39834     
39835     beforeSlide : function(){
39836         this.el.clip();
39837         this.resizeEl.clip();
39838     },
39839     
39840     afterSlide : function(){
39841         this.el.unclip();
39842         this.resizeEl.unclip();
39843     },
39844     
39845     /**
39846      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39847      *   Will fail silently if the {@link #setUrl} method has not been called.
39848      *   This does not activate the panel, just updates its content.
39849      */
39850     refresh : function(){
39851         if(this.refreshDelegate){
39852            this.loaded = false;
39853            this.refreshDelegate();
39854         }
39855     },
39856     
39857     /**
39858      * Destroys this panel
39859      */
39860     destroy : function(){
39861         this.el.removeAllListeners();
39862         var tempEl = document.createElement("span");
39863         tempEl.appendChild(this.el.dom);
39864         tempEl.innerHTML = "";
39865         this.el.remove();
39866         this.el = null;
39867     },
39868     
39869     /**
39870      * form - if the content panel contains a form - this is a reference to it.
39871      * @type {Roo.form.Form}
39872      */
39873     form : false,
39874     /**
39875      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39876      *    This contains a reference to it.
39877      * @type {Roo.View}
39878      */
39879     view : false,
39880     
39881       /**
39882      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39883      * <pre><code>
39884
39885 layout.addxtype({
39886        xtype : 'Form',
39887        items: [ .... ]
39888    }
39889 );
39890
39891 </code></pre>
39892      * @param {Object} cfg Xtype definition of item to add.
39893      */
39894     
39895     
39896     getChildContainer: function () {
39897         return this.getEl();
39898     }
39899     
39900     
39901     /*
39902         var  ret = new Roo.factory(cfg);
39903         return ret;
39904         
39905         
39906         // add form..
39907         if (cfg.xtype.match(/^Form$/)) {
39908             
39909             var el;
39910             //if (this.footer) {
39911             //    el = this.footer.container.insertSibling(false, 'before');
39912             //} else {
39913                 el = this.el.createChild();
39914             //}
39915
39916             this.form = new  Roo.form.Form(cfg);
39917             
39918             
39919             if ( this.form.allItems.length) {
39920                 this.form.render(el.dom);
39921             }
39922             return this.form;
39923         }
39924         // should only have one of theses..
39925         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39926             // views.. should not be just added - used named prop 'view''
39927             
39928             cfg.el = this.el.appendChild(document.createElement("div"));
39929             // factory?
39930             
39931             var ret = new Roo.factory(cfg);
39932              
39933              ret.render && ret.render(false, ''); // render blank..
39934             this.view = ret;
39935             return ret;
39936         }
39937         return false;
39938     }
39939     \*/
39940 });
39941  
39942 /**
39943  * @class Roo.bootstrap.panel.Grid
39944  * @extends Roo.bootstrap.panel.Content
39945  * @constructor
39946  * Create a new GridPanel.
39947  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39948  * @param {Object} config A the config object
39949   
39950  */
39951
39952
39953
39954 Roo.bootstrap.panel.Grid = function(config)
39955 {
39956     
39957       
39958     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39959         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39960
39961     config.el = this.wrapper;
39962     //this.el = this.wrapper;
39963     
39964       if (config.container) {
39965         // ctor'ed from a Border/panel.grid
39966         
39967         
39968         this.wrapper.setStyle("overflow", "hidden");
39969         this.wrapper.addClass('roo-grid-container');
39970
39971     }
39972     
39973     
39974     if(config.toolbar){
39975         var tool_el = this.wrapper.createChild();    
39976         this.toolbar = Roo.factory(config.toolbar);
39977         var ti = [];
39978         if (config.toolbar.items) {
39979             ti = config.toolbar.items ;
39980             delete config.toolbar.items ;
39981         }
39982         
39983         var nitems = [];
39984         this.toolbar.render(tool_el);
39985         for(var i =0;i < ti.length;i++) {
39986           //  Roo.log(['add child', items[i]]);
39987             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39988         }
39989         this.toolbar.items = nitems;
39990         
39991         delete config.toolbar;
39992     }
39993     
39994     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39995     config.grid.scrollBody = true;;
39996     config.grid.monitorWindowResize = false; // turn off autosizing
39997     config.grid.autoHeight = false;
39998     config.grid.autoWidth = false;
39999     
40000     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
40001     
40002     if (config.background) {
40003         // render grid on panel activation (if panel background)
40004         this.on('activate', function(gp) {
40005             if (!gp.grid.rendered) {
40006                 gp.grid.render(this.wrapper);
40007                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40008             }
40009         });
40010             
40011     } else {
40012         this.grid.render(this.wrapper);
40013         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40014
40015     }
40016     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40017     // ??? needed ??? config.el = this.wrapper;
40018     
40019     
40020     
40021   
40022     // xtype created footer. - not sure if will work as we normally have to render first..
40023     if (this.footer && !this.footer.el && this.footer.xtype) {
40024         
40025         var ctr = this.grid.getView().getFooterPanel(true);
40026         this.footer.dataSource = this.grid.dataSource;
40027         this.footer = Roo.factory(this.footer, Roo);
40028         this.footer.render(ctr);
40029         
40030     }
40031     
40032     
40033     
40034     
40035      
40036 };
40037
40038 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40039     getId : function(){
40040         return this.grid.id;
40041     },
40042     
40043     /**
40044      * Returns the grid for this panel
40045      * @return {Roo.bootstrap.Table} 
40046      */
40047     getGrid : function(){
40048         return this.grid;    
40049     },
40050     
40051     setSize : function(width, height){
40052         if(!this.ignoreResize(width, height)){
40053             var grid = this.grid;
40054             var size = this.adjustForComponents(width, height);
40055             // tfoot is not a footer?
40056           
40057             
40058             var gridel = grid.getGridEl();
40059             gridel.setSize(size.width, size.height);
40060             
40061             var tbd = grid.getGridEl().select('tbody', true).first();
40062             var thd = grid.getGridEl().select('thead',true).first();
40063             var tbf= grid.getGridEl().select('tfoot', true).first();
40064
40065             if (tbf) {
40066                 size.height -= thd.getHeight();
40067             }
40068             if (thd) {
40069                 size.height -= thd.getHeight();
40070             }
40071             
40072             tbd.setSize(size.width, size.height );
40073             // this is for the account management tab -seems to work there.
40074             var thd = grid.getGridEl().select('thead',true).first();
40075             //if (tbd) {
40076             //    tbd.setSize(size.width, size.height - thd.getHeight());
40077             //}
40078              
40079             grid.autoSize();
40080         }
40081     },
40082      
40083     
40084     
40085     beforeSlide : function(){
40086         this.grid.getView().scroller.clip();
40087     },
40088     
40089     afterSlide : function(){
40090         this.grid.getView().scroller.unclip();
40091     },
40092     
40093     destroy : function(){
40094         this.grid.destroy();
40095         delete this.grid;
40096         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40097     }
40098 });
40099
40100 /**
40101  * @class Roo.bootstrap.panel.Nest
40102  * @extends Roo.bootstrap.panel.Content
40103  * @constructor
40104  * Create a new Panel, that can contain a layout.Border.
40105  * 
40106  * 
40107  * @param {Roo.BorderLayout} layout The layout for this panel
40108  * @param {String/Object} config A string to set only the title or a config object
40109  */
40110 Roo.bootstrap.panel.Nest = function(config)
40111 {
40112     // construct with only one argument..
40113     /* FIXME - implement nicer consturctors
40114     if (layout.layout) {
40115         config = layout;
40116         layout = config.layout;
40117         delete config.layout;
40118     }
40119     if (layout.xtype && !layout.getEl) {
40120         // then layout needs constructing..
40121         layout = Roo.factory(layout, Roo);
40122     }
40123     */
40124     
40125     config.el =  config.layout.getEl();
40126     
40127     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40128     
40129     config.layout.monitorWindowResize = false; // turn off autosizing
40130     this.layout = config.layout;
40131     this.layout.getEl().addClass("roo-layout-nested-layout");
40132     this.layout.parent = this;
40133     
40134     
40135     
40136     
40137 };
40138
40139 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40140
40141     setSize : function(width, height){
40142         if(!this.ignoreResize(width, height)){
40143             var size = this.adjustForComponents(width, height);
40144             var el = this.layout.getEl();
40145             if (size.height < 1) {
40146                 el.setWidth(size.width);   
40147             } else {
40148                 el.setSize(size.width, size.height);
40149             }
40150             var touch = el.dom.offsetWidth;
40151             this.layout.layout();
40152             // ie requires a double layout on the first pass
40153             if(Roo.isIE && !this.initialized){
40154                 this.initialized = true;
40155                 this.layout.layout();
40156             }
40157         }
40158     },
40159     
40160     // activate all subpanels if not currently active..
40161     
40162     setActiveState : function(active){
40163         this.active = active;
40164         this.setActiveClass(active);
40165         
40166         if(!active){
40167             this.fireEvent("deactivate", this);
40168             return;
40169         }
40170         
40171         this.fireEvent("activate", this);
40172         // not sure if this should happen before or after..
40173         if (!this.layout) {
40174             return; // should not happen..
40175         }
40176         var reg = false;
40177         for (var r in this.layout.regions) {
40178             reg = this.layout.getRegion(r);
40179             if (reg.getActivePanel()) {
40180                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40181                 reg.setActivePanel(reg.getActivePanel());
40182                 continue;
40183             }
40184             if (!reg.panels.length) {
40185                 continue;
40186             }
40187             reg.showPanel(reg.getPanel(0));
40188         }
40189         
40190         
40191         
40192         
40193     },
40194     
40195     /**
40196      * Returns the nested BorderLayout for this panel
40197      * @return {Roo.BorderLayout} 
40198      */
40199     getLayout : function(){
40200         return this.layout;
40201     },
40202     
40203      /**
40204      * Adds a xtype elements to the layout of the nested panel
40205      * <pre><code>
40206
40207 panel.addxtype({
40208        xtype : 'ContentPanel',
40209        region: 'west',
40210        items: [ .... ]
40211    }
40212 );
40213
40214 panel.addxtype({
40215         xtype : 'NestedLayoutPanel',
40216         region: 'west',
40217         layout: {
40218            center: { },
40219            west: { }   
40220         },
40221         items : [ ... list of content panels or nested layout panels.. ]
40222    }
40223 );
40224 </code></pre>
40225      * @param {Object} cfg Xtype definition of item to add.
40226      */
40227     addxtype : function(cfg) {
40228         return this.layout.addxtype(cfg);
40229     
40230     }
40231 });/*
40232  * Based on:
40233  * Ext JS Library 1.1.1
40234  * Copyright(c) 2006-2007, Ext JS, LLC.
40235  *
40236  * Originally Released Under LGPL - original licence link has changed is not relivant.
40237  *
40238  * Fork - LGPL
40239  * <script type="text/javascript">
40240  */
40241 /**
40242  * @class Roo.TabPanel
40243  * @extends Roo.util.Observable
40244  * A lightweight tab container.
40245  * <br><br>
40246  * Usage:
40247  * <pre><code>
40248 // basic tabs 1, built from existing content
40249 var tabs = new Roo.TabPanel("tabs1");
40250 tabs.addTab("script", "View Script");
40251 tabs.addTab("markup", "View Markup");
40252 tabs.activate("script");
40253
40254 // more advanced tabs, built from javascript
40255 var jtabs = new Roo.TabPanel("jtabs");
40256 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40257
40258 // set up the UpdateManager
40259 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40260 var updater = tab2.getUpdateManager();
40261 updater.setDefaultUrl("ajax1.htm");
40262 tab2.on('activate', updater.refresh, updater, true);
40263
40264 // Use setUrl for Ajax loading
40265 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40266 tab3.setUrl("ajax2.htm", null, true);
40267
40268 // Disabled tab
40269 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40270 tab4.disable();
40271
40272 jtabs.activate("jtabs-1");
40273  * </code></pre>
40274  * @constructor
40275  * Create a new TabPanel.
40276  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40277  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40278  */
40279 Roo.bootstrap.panel.Tabs = function(config){
40280     /**
40281     * The container element for this TabPanel.
40282     * @type Roo.Element
40283     */
40284     this.el = Roo.get(config.el);
40285     delete config.el;
40286     if(config){
40287         if(typeof config == "boolean"){
40288             this.tabPosition = config ? "bottom" : "top";
40289         }else{
40290             Roo.apply(this, config);
40291         }
40292     }
40293     
40294     if(this.tabPosition == "bottom"){
40295         // if tabs are at the bottom = create the body first.
40296         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40297         this.el.addClass("roo-tabs-bottom");
40298     }
40299     // next create the tabs holders
40300     
40301     if (this.tabPosition == "west"){
40302         
40303         var reg = this.region; // fake it..
40304         while (reg) {
40305             if (!reg.mgr.parent) {
40306                 break;
40307             }
40308             reg = reg.mgr.parent.region;
40309         }
40310         Roo.log("got nest?");
40311         Roo.log(reg);
40312         if (reg.mgr.getRegion('west')) {
40313             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40314             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40315             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40316             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40317             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40318         
40319             
40320         }
40321         
40322         
40323     } else {
40324      
40325         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40326         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40327         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40328         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40329     }
40330     
40331     
40332     if(Roo.isIE){
40333         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40334     }
40335     
40336     // finally - if tabs are at the top, then create the body last..
40337     if(this.tabPosition != "bottom"){
40338         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40339          * @type Roo.Element
40340          */
40341         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40342         this.el.addClass("roo-tabs-top");
40343     }
40344     this.items = [];
40345
40346     this.bodyEl.setStyle("position", "relative");
40347
40348     this.active = null;
40349     this.activateDelegate = this.activate.createDelegate(this);
40350
40351     this.addEvents({
40352         /**
40353          * @event tabchange
40354          * Fires when the active tab changes
40355          * @param {Roo.TabPanel} this
40356          * @param {Roo.TabPanelItem} activePanel The new active tab
40357          */
40358         "tabchange": true,
40359         /**
40360          * @event beforetabchange
40361          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40362          * @param {Roo.TabPanel} this
40363          * @param {Object} e Set cancel to true on this object to cancel the tab change
40364          * @param {Roo.TabPanelItem} tab The tab being changed to
40365          */
40366         "beforetabchange" : true
40367     });
40368
40369     Roo.EventManager.onWindowResize(this.onResize, this);
40370     this.cpad = this.el.getPadding("lr");
40371     this.hiddenCount = 0;
40372
40373
40374     // toolbar on the tabbar support...
40375     if (this.toolbar) {
40376         alert("no toolbar support yet");
40377         this.toolbar  = false;
40378         /*
40379         var tcfg = this.toolbar;
40380         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40381         this.toolbar = new Roo.Toolbar(tcfg);
40382         if (Roo.isSafari) {
40383             var tbl = tcfg.container.child('table', true);
40384             tbl.setAttribute('width', '100%');
40385         }
40386         */
40387         
40388     }
40389    
40390
40391
40392     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40393 };
40394
40395 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40396     /*
40397      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40398      */
40399     tabPosition : "top",
40400     /*
40401      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40402      */
40403     currentTabWidth : 0,
40404     /*
40405      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40406      */
40407     minTabWidth : 40,
40408     /*
40409      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40410      */
40411     maxTabWidth : 250,
40412     /*
40413      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40414      */
40415     preferredTabWidth : 175,
40416     /*
40417      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40418      */
40419     resizeTabs : false,
40420     /*
40421      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40422      */
40423     monitorResize : true,
40424     /*
40425      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40426      */
40427     toolbar : false,  // set by caller..
40428     
40429     region : false, /// set by caller
40430     
40431     disableTooltips : true, // not used yet...
40432
40433     /**
40434      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40435      * @param {String} id The id of the div to use <b>or create</b>
40436      * @param {String} text The text for the tab
40437      * @param {String} content (optional) Content to put in the TabPanelItem body
40438      * @param {Boolean} closable (optional) True to create a close icon on the tab
40439      * @return {Roo.TabPanelItem} The created TabPanelItem
40440      */
40441     addTab : function(id, text, content, closable, tpl)
40442     {
40443         var item = new Roo.bootstrap.panel.TabItem({
40444             panel: this,
40445             id : id,
40446             text : text,
40447             closable : closable,
40448             tpl : tpl
40449         });
40450         this.addTabItem(item);
40451         if(content){
40452             item.setContent(content);
40453         }
40454         return item;
40455     },
40456
40457     /**
40458      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40459      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40460      * @return {Roo.TabPanelItem}
40461      */
40462     getTab : function(id){
40463         return this.items[id];
40464     },
40465
40466     /**
40467      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40468      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40469      */
40470     hideTab : function(id){
40471         var t = this.items[id];
40472         if(!t.isHidden()){
40473            t.setHidden(true);
40474            this.hiddenCount++;
40475            this.autoSizeTabs();
40476         }
40477     },
40478
40479     /**
40480      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40481      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40482      */
40483     unhideTab : function(id){
40484         var t = this.items[id];
40485         if(t.isHidden()){
40486            t.setHidden(false);
40487            this.hiddenCount--;
40488            this.autoSizeTabs();
40489         }
40490     },
40491
40492     /**
40493      * Adds an existing {@link Roo.TabPanelItem}.
40494      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40495      */
40496     addTabItem : function(item)
40497     {
40498         this.items[item.id] = item;
40499         this.items.push(item);
40500         this.autoSizeTabs();
40501       //  if(this.resizeTabs){
40502     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40503   //         this.autoSizeTabs();
40504 //        }else{
40505 //            item.autoSize();
40506        // }
40507     },
40508
40509     /**
40510      * Removes a {@link Roo.TabPanelItem}.
40511      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40512      */
40513     removeTab : function(id){
40514         var items = this.items;
40515         var tab = items[id];
40516         if(!tab) { return; }
40517         var index = items.indexOf(tab);
40518         if(this.active == tab && items.length > 1){
40519             var newTab = this.getNextAvailable(index);
40520             if(newTab) {
40521                 newTab.activate();
40522             }
40523         }
40524         this.stripEl.dom.removeChild(tab.pnode.dom);
40525         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40526             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40527         }
40528         items.splice(index, 1);
40529         delete this.items[tab.id];
40530         tab.fireEvent("close", tab);
40531         tab.purgeListeners();
40532         this.autoSizeTabs();
40533     },
40534
40535     getNextAvailable : function(start){
40536         var items = this.items;
40537         var index = start;
40538         // look for a next tab that will slide over to
40539         // replace the one being removed
40540         while(index < items.length){
40541             var item = items[++index];
40542             if(item && !item.isHidden()){
40543                 return item;
40544             }
40545         }
40546         // if one isn't found select the previous tab (on the left)
40547         index = start;
40548         while(index >= 0){
40549             var item = items[--index];
40550             if(item && !item.isHidden()){
40551                 return item;
40552             }
40553         }
40554         return null;
40555     },
40556
40557     /**
40558      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40559      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40560      */
40561     disableTab : function(id){
40562         var tab = this.items[id];
40563         if(tab && this.active != tab){
40564             tab.disable();
40565         }
40566     },
40567
40568     /**
40569      * Enables a {@link Roo.TabPanelItem} that is disabled.
40570      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40571      */
40572     enableTab : function(id){
40573         var tab = this.items[id];
40574         tab.enable();
40575     },
40576
40577     /**
40578      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40579      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40580      * @return {Roo.TabPanelItem} The TabPanelItem.
40581      */
40582     activate : function(id)
40583     {
40584         //Roo.log('activite:'  + id);
40585         
40586         var tab = this.items[id];
40587         if(!tab){
40588             return null;
40589         }
40590         if(tab == this.active || tab.disabled){
40591             return tab;
40592         }
40593         var e = {};
40594         this.fireEvent("beforetabchange", this, e, tab);
40595         if(e.cancel !== true && !tab.disabled){
40596             if(this.active){
40597                 this.active.hide();
40598             }
40599             this.active = this.items[id];
40600             this.active.show();
40601             this.fireEvent("tabchange", this, this.active);
40602         }
40603         return tab;
40604     },
40605
40606     /**
40607      * Gets the active {@link Roo.TabPanelItem}.
40608      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40609      */
40610     getActiveTab : function(){
40611         return this.active;
40612     },
40613
40614     /**
40615      * Updates the tab body element to fit the height of the container element
40616      * for overflow scrolling
40617      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40618      */
40619     syncHeight : function(targetHeight){
40620         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40621         var bm = this.bodyEl.getMargins();
40622         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40623         this.bodyEl.setHeight(newHeight);
40624         return newHeight;
40625     },
40626
40627     onResize : function(){
40628         if(this.monitorResize){
40629             this.autoSizeTabs();
40630         }
40631     },
40632
40633     /**
40634      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40635      */
40636     beginUpdate : function(){
40637         this.updating = true;
40638     },
40639
40640     /**
40641      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40642      */
40643     endUpdate : function(){
40644         this.updating = false;
40645         this.autoSizeTabs();
40646     },
40647
40648     /**
40649      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40650      */
40651     autoSizeTabs : function()
40652     {
40653         var count = this.items.length;
40654         var vcount = count - this.hiddenCount;
40655         
40656         if (vcount < 2) {
40657             this.stripEl.hide();
40658         } else {
40659             this.stripEl.show();
40660         }
40661         
40662         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40663             return;
40664         }
40665         
40666         
40667         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40668         var availWidth = Math.floor(w / vcount);
40669         var b = this.stripBody;
40670         if(b.getWidth() > w){
40671             var tabs = this.items;
40672             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40673             if(availWidth < this.minTabWidth){
40674                 /*if(!this.sleft){    // incomplete scrolling code
40675                     this.createScrollButtons();
40676                 }
40677                 this.showScroll();
40678                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40679             }
40680         }else{
40681             if(this.currentTabWidth < this.preferredTabWidth){
40682                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40683             }
40684         }
40685     },
40686
40687     /**
40688      * Returns the number of tabs in this TabPanel.
40689      * @return {Number}
40690      */
40691      getCount : function(){
40692          return this.items.length;
40693      },
40694
40695     /**
40696      * Resizes all the tabs to the passed width
40697      * @param {Number} The new width
40698      */
40699     setTabWidth : function(width){
40700         this.currentTabWidth = width;
40701         for(var i = 0, len = this.items.length; i < len; i++) {
40702                 if(!this.items[i].isHidden()) {
40703                 this.items[i].setWidth(width);
40704             }
40705         }
40706     },
40707
40708     /**
40709      * Destroys this TabPanel
40710      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40711      */
40712     destroy : function(removeEl){
40713         Roo.EventManager.removeResizeListener(this.onResize, this);
40714         for(var i = 0, len = this.items.length; i < len; i++){
40715             this.items[i].purgeListeners();
40716         }
40717         if(removeEl === true){
40718             this.el.update("");
40719             this.el.remove();
40720         }
40721     },
40722     
40723     createStrip : function(container)
40724     {
40725         var strip = document.createElement("nav");
40726         strip.className = Roo.bootstrap.version == 4 ?
40727             "navbar-light bg-light" : 
40728             "navbar navbar-default"; //"x-tabs-wrap";
40729         container.appendChild(strip);
40730         return strip;
40731     },
40732     
40733     createStripList : function(strip)
40734     {
40735         // div wrapper for retard IE
40736         // returns the "tr" element.
40737         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40738         //'<div class="x-tabs-strip-wrap">'+
40739           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40740           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40741         return strip.firstChild; //.firstChild.firstChild.firstChild;
40742     },
40743     createBody : function(container)
40744     {
40745         var body = document.createElement("div");
40746         Roo.id(body, "tab-body");
40747         //Roo.fly(body).addClass("x-tabs-body");
40748         Roo.fly(body).addClass("tab-content");
40749         container.appendChild(body);
40750         return body;
40751     },
40752     createItemBody :function(bodyEl, id){
40753         var body = Roo.getDom(id);
40754         if(!body){
40755             body = document.createElement("div");
40756             body.id = id;
40757         }
40758         //Roo.fly(body).addClass("x-tabs-item-body");
40759         Roo.fly(body).addClass("tab-pane");
40760          bodyEl.insertBefore(body, bodyEl.firstChild);
40761         return body;
40762     },
40763     /** @private */
40764     createStripElements :  function(stripEl, text, closable, tpl)
40765     {
40766         var td = document.createElement("li"); // was td..
40767         td.className = 'nav-item';
40768         
40769         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40770         
40771         
40772         stripEl.appendChild(td);
40773         /*if(closable){
40774             td.className = "x-tabs-closable";
40775             if(!this.closeTpl){
40776                 this.closeTpl = new Roo.Template(
40777                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40778                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40779                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40780                 );
40781             }
40782             var el = this.closeTpl.overwrite(td, {"text": text});
40783             var close = el.getElementsByTagName("div")[0];
40784             var inner = el.getElementsByTagName("em")[0];
40785             return {"el": el, "close": close, "inner": inner};
40786         } else {
40787         */
40788         // not sure what this is..
40789 //            if(!this.tabTpl){
40790                 //this.tabTpl = new Roo.Template(
40791                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40792                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40793                 //);
40794 //                this.tabTpl = new Roo.Template(
40795 //                   '<a href="#">' +
40796 //                   '<span unselectable="on"' +
40797 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40798 //                            ' >{text}</span></a>'
40799 //                );
40800 //                
40801 //            }
40802
40803
40804             var template = tpl || this.tabTpl || false;
40805             
40806             if(!template){
40807                 template =  new Roo.Template(
40808                         Roo.bootstrap.version == 4 ? 
40809                             (
40810                                 '<a class="nav-link" href="#" unselectable="on"' +
40811                                      (this.disableTooltips ? '' : ' title="{text}"') +
40812                                      ' >{text}</a>'
40813                             ) : (
40814                                 '<a class="nav-link" href="#">' +
40815                                 '<span unselectable="on"' +
40816                                          (this.disableTooltips ? '' : ' title="{text}"') +
40817                                     ' >{text}</span></a>'
40818                             )
40819                 );
40820             }
40821             
40822             switch (typeof(template)) {
40823                 case 'object' :
40824                     break;
40825                 case 'string' :
40826                     template = new Roo.Template(template);
40827                     break;
40828                 default :
40829                     break;
40830             }
40831             
40832             var el = template.overwrite(td, {"text": text});
40833             
40834             var inner = el.getElementsByTagName("span")[0];
40835             
40836             return {"el": el, "inner": inner};
40837             
40838     }
40839         
40840     
40841 });
40842
40843 /**
40844  * @class Roo.TabPanelItem
40845  * @extends Roo.util.Observable
40846  * Represents an individual item (tab plus body) in a TabPanel.
40847  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40848  * @param {String} id The id of this TabPanelItem
40849  * @param {String} text The text for the tab of this TabPanelItem
40850  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40851  */
40852 Roo.bootstrap.panel.TabItem = function(config){
40853     /**
40854      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40855      * @type Roo.TabPanel
40856      */
40857     this.tabPanel = config.panel;
40858     /**
40859      * The id for this TabPanelItem
40860      * @type String
40861      */
40862     this.id = config.id;
40863     /** @private */
40864     this.disabled = false;
40865     /** @private */
40866     this.text = config.text;
40867     /** @private */
40868     this.loaded = false;
40869     this.closable = config.closable;
40870
40871     /**
40872      * The body element for this TabPanelItem.
40873      * @type Roo.Element
40874      */
40875     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40876     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40877     this.bodyEl.setStyle("display", "block");
40878     this.bodyEl.setStyle("zoom", "1");
40879     //this.hideAction();
40880
40881     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40882     /** @private */
40883     this.el = Roo.get(els.el);
40884     this.inner = Roo.get(els.inner, true);
40885      this.textEl = Roo.bootstrap.version == 4 ?
40886         this.el : Roo.get(this.el.dom.firstChild, true);
40887
40888     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40889     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40890
40891     
40892 //    this.el.on("mousedown", this.onTabMouseDown, this);
40893     this.el.on("click", this.onTabClick, this);
40894     /** @private */
40895     if(config.closable){
40896         var c = Roo.get(els.close, true);
40897         c.dom.title = this.closeText;
40898         c.addClassOnOver("close-over");
40899         c.on("click", this.closeClick, this);
40900      }
40901
40902     this.addEvents({
40903          /**
40904          * @event activate
40905          * Fires when this tab becomes the active tab.
40906          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40907          * @param {Roo.TabPanelItem} this
40908          */
40909         "activate": true,
40910         /**
40911          * @event beforeclose
40912          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40913          * @param {Roo.TabPanelItem} this
40914          * @param {Object} e Set cancel to true on this object to cancel the close.
40915          */
40916         "beforeclose": true,
40917         /**
40918          * @event close
40919          * Fires when this tab is closed.
40920          * @param {Roo.TabPanelItem} this
40921          */
40922          "close": true,
40923         /**
40924          * @event deactivate
40925          * Fires when this tab is no longer the active tab.
40926          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40927          * @param {Roo.TabPanelItem} this
40928          */
40929          "deactivate" : true
40930     });
40931     this.hidden = false;
40932
40933     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40934 };
40935
40936 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40937            {
40938     purgeListeners : function(){
40939        Roo.util.Observable.prototype.purgeListeners.call(this);
40940        this.el.removeAllListeners();
40941     },
40942     /**
40943      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40944      */
40945     show : function(){
40946         this.status_node.addClass("active");
40947         this.showAction();
40948         if(Roo.isOpera){
40949             this.tabPanel.stripWrap.repaint();
40950         }
40951         this.fireEvent("activate", this.tabPanel, this);
40952     },
40953
40954     /**
40955      * Returns true if this tab is the active tab.
40956      * @return {Boolean}
40957      */
40958     isActive : function(){
40959         return this.tabPanel.getActiveTab() == this;
40960     },
40961
40962     /**
40963      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40964      */
40965     hide : function(){
40966         this.status_node.removeClass("active");
40967         this.hideAction();
40968         this.fireEvent("deactivate", this.tabPanel, this);
40969     },
40970
40971     hideAction : function(){
40972         this.bodyEl.hide();
40973         this.bodyEl.setStyle("position", "absolute");
40974         this.bodyEl.setLeft("-20000px");
40975         this.bodyEl.setTop("-20000px");
40976     },
40977
40978     showAction : function(){
40979         this.bodyEl.setStyle("position", "relative");
40980         this.bodyEl.setTop("");
40981         this.bodyEl.setLeft("");
40982         this.bodyEl.show();
40983     },
40984
40985     /**
40986      * Set the tooltip for the tab.
40987      * @param {String} tooltip The tab's tooltip
40988      */
40989     setTooltip : function(text){
40990         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40991             this.textEl.dom.qtip = text;
40992             this.textEl.dom.removeAttribute('title');
40993         }else{
40994             this.textEl.dom.title = text;
40995         }
40996     },
40997
40998     onTabClick : function(e){
40999         e.preventDefault();
41000         this.tabPanel.activate(this.id);
41001     },
41002
41003     onTabMouseDown : function(e){
41004         e.preventDefault();
41005         this.tabPanel.activate(this.id);
41006     },
41007 /*
41008     getWidth : function(){
41009         return this.inner.getWidth();
41010     },
41011
41012     setWidth : function(width){
41013         var iwidth = width - this.linode.getPadding("lr");
41014         this.inner.setWidth(iwidth);
41015         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41016         this.linode.setWidth(width);
41017     },
41018 */
41019     /**
41020      * Show or hide the tab
41021      * @param {Boolean} hidden True to hide or false to show.
41022      */
41023     setHidden : function(hidden){
41024         this.hidden = hidden;
41025         this.linode.setStyle("display", hidden ? "none" : "");
41026     },
41027
41028     /**
41029      * Returns true if this tab is "hidden"
41030      * @return {Boolean}
41031      */
41032     isHidden : function(){
41033         return this.hidden;
41034     },
41035
41036     /**
41037      * Returns the text for this tab
41038      * @return {String}
41039      */
41040     getText : function(){
41041         return this.text;
41042     },
41043     /*
41044     autoSize : function(){
41045         //this.el.beginMeasure();
41046         this.textEl.setWidth(1);
41047         /*
41048          *  #2804 [new] Tabs in Roojs
41049          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41050          */
41051         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41052         //this.el.endMeasure();
41053     //},
41054
41055     /**
41056      * Sets the text for the tab (Note: this also sets the tooltip text)
41057      * @param {String} text The tab's text and tooltip
41058      */
41059     setText : function(text){
41060         this.text = text;
41061         this.textEl.update(text);
41062         this.setTooltip(text);
41063         //if(!this.tabPanel.resizeTabs){
41064         //    this.autoSize();
41065         //}
41066     },
41067     /**
41068      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41069      */
41070     activate : function(){
41071         this.tabPanel.activate(this.id);
41072     },
41073
41074     /**
41075      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41076      */
41077     disable : function(){
41078         if(this.tabPanel.active != this){
41079             this.disabled = true;
41080             this.status_node.addClass("disabled");
41081         }
41082     },
41083
41084     /**
41085      * Enables this TabPanelItem if it was previously disabled.
41086      */
41087     enable : function(){
41088         this.disabled = false;
41089         this.status_node.removeClass("disabled");
41090     },
41091
41092     /**
41093      * Sets the content for this TabPanelItem.
41094      * @param {String} content The content
41095      * @param {Boolean} loadScripts true to look for and load scripts
41096      */
41097     setContent : function(content, loadScripts){
41098         this.bodyEl.update(content, loadScripts);
41099     },
41100
41101     /**
41102      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41103      * @return {Roo.UpdateManager} The UpdateManager
41104      */
41105     getUpdateManager : function(){
41106         return this.bodyEl.getUpdateManager();
41107     },
41108
41109     /**
41110      * Set a URL to be used to load the content for this TabPanelItem.
41111      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41112      * @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)
41113      * @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)
41114      * @return {Roo.UpdateManager} The UpdateManager
41115      */
41116     setUrl : function(url, params, loadOnce){
41117         if(this.refreshDelegate){
41118             this.un('activate', this.refreshDelegate);
41119         }
41120         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41121         this.on("activate", this.refreshDelegate);
41122         return this.bodyEl.getUpdateManager();
41123     },
41124
41125     /** @private */
41126     _handleRefresh : function(url, params, loadOnce){
41127         if(!loadOnce || !this.loaded){
41128             var updater = this.bodyEl.getUpdateManager();
41129             updater.update(url, params, this._setLoaded.createDelegate(this));
41130         }
41131     },
41132
41133     /**
41134      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41135      *   Will fail silently if the setUrl method has not been called.
41136      *   This does not activate the panel, just updates its content.
41137      */
41138     refresh : function(){
41139         if(this.refreshDelegate){
41140            this.loaded = false;
41141            this.refreshDelegate();
41142         }
41143     },
41144
41145     /** @private */
41146     _setLoaded : function(){
41147         this.loaded = true;
41148     },
41149
41150     /** @private */
41151     closeClick : function(e){
41152         var o = {};
41153         e.stopEvent();
41154         this.fireEvent("beforeclose", this, o);
41155         if(o.cancel !== true){
41156             this.tabPanel.removeTab(this.id);
41157         }
41158     },
41159     /**
41160      * The text displayed in the tooltip for the close icon.
41161      * @type String
41162      */
41163     closeText : "Close this tab"
41164 });
41165 /**
41166 *    This script refer to:
41167 *    Title: International Telephone Input
41168 *    Author: Jack O'Connor
41169 *    Code version:  v12.1.12
41170 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41171 **/
41172
41173 Roo.bootstrap.PhoneInputData = function() {
41174     var d = [
41175       [
41176         "Afghanistan (‫افغانستان‬‎)",
41177         "af",
41178         "93"
41179       ],
41180       [
41181         "Albania (Shqipëri)",
41182         "al",
41183         "355"
41184       ],
41185       [
41186         "Algeria (‫الجزائر‬‎)",
41187         "dz",
41188         "213"
41189       ],
41190       [
41191         "American Samoa",
41192         "as",
41193         "1684"
41194       ],
41195       [
41196         "Andorra",
41197         "ad",
41198         "376"
41199       ],
41200       [
41201         "Angola",
41202         "ao",
41203         "244"
41204       ],
41205       [
41206         "Anguilla",
41207         "ai",
41208         "1264"
41209       ],
41210       [
41211         "Antigua and Barbuda",
41212         "ag",
41213         "1268"
41214       ],
41215       [
41216         "Argentina",
41217         "ar",
41218         "54"
41219       ],
41220       [
41221         "Armenia (Հայաստան)",
41222         "am",
41223         "374"
41224       ],
41225       [
41226         "Aruba",
41227         "aw",
41228         "297"
41229       ],
41230       [
41231         "Australia",
41232         "au",
41233         "61",
41234         0
41235       ],
41236       [
41237         "Austria (Österreich)",
41238         "at",
41239         "43"
41240       ],
41241       [
41242         "Azerbaijan (Azərbaycan)",
41243         "az",
41244         "994"
41245       ],
41246       [
41247         "Bahamas",
41248         "bs",
41249         "1242"
41250       ],
41251       [
41252         "Bahrain (‫البحرين‬‎)",
41253         "bh",
41254         "973"
41255       ],
41256       [
41257         "Bangladesh (বাংলাদেশ)",
41258         "bd",
41259         "880"
41260       ],
41261       [
41262         "Barbados",
41263         "bb",
41264         "1246"
41265       ],
41266       [
41267         "Belarus (Беларусь)",
41268         "by",
41269         "375"
41270       ],
41271       [
41272         "Belgium (België)",
41273         "be",
41274         "32"
41275       ],
41276       [
41277         "Belize",
41278         "bz",
41279         "501"
41280       ],
41281       [
41282         "Benin (Bénin)",
41283         "bj",
41284         "229"
41285       ],
41286       [
41287         "Bermuda",
41288         "bm",
41289         "1441"
41290       ],
41291       [
41292         "Bhutan (འབྲུག)",
41293         "bt",
41294         "975"
41295       ],
41296       [
41297         "Bolivia",
41298         "bo",
41299         "591"
41300       ],
41301       [
41302         "Bosnia and Herzegovina (Босна и Херцеговина)",
41303         "ba",
41304         "387"
41305       ],
41306       [
41307         "Botswana",
41308         "bw",
41309         "267"
41310       ],
41311       [
41312         "Brazil (Brasil)",
41313         "br",
41314         "55"
41315       ],
41316       [
41317         "British Indian Ocean Territory",
41318         "io",
41319         "246"
41320       ],
41321       [
41322         "British Virgin Islands",
41323         "vg",
41324         "1284"
41325       ],
41326       [
41327         "Brunei",
41328         "bn",
41329         "673"
41330       ],
41331       [
41332         "Bulgaria (България)",
41333         "bg",
41334         "359"
41335       ],
41336       [
41337         "Burkina Faso",
41338         "bf",
41339         "226"
41340       ],
41341       [
41342         "Burundi (Uburundi)",
41343         "bi",
41344         "257"
41345       ],
41346       [
41347         "Cambodia (កម្ពុជា)",
41348         "kh",
41349         "855"
41350       ],
41351       [
41352         "Cameroon (Cameroun)",
41353         "cm",
41354         "237"
41355       ],
41356       [
41357         "Canada",
41358         "ca",
41359         "1",
41360         1,
41361         ["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"]
41362       ],
41363       [
41364         "Cape Verde (Kabu Verdi)",
41365         "cv",
41366         "238"
41367       ],
41368       [
41369         "Caribbean Netherlands",
41370         "bq",
41371         "599",
41372         1
41373       ],
41374       [
41375         "Cayman Islands",
41376         "ky",
41377         "1345"
41378       ],
41379       [
41380         "Central African Republic (République centrafricaine)",
41381         "cf",
41382         "236"
41383       ],
41384       [
41385         "Chad (Tchad)",
41386         "td",
41387         "235"
41388       ],
41389       [
41390         "Chile",
41391         "cl",
41392         "56"
41393       ],
41394       [
41395         "China (中国)",
41396         "cn",
41397         "86"
41398       ],
41399       [
41400         "Christmas Island",
41401         "cx",
41402         "61",
41403         2
41404       ],
41405       [
41406         "Cocos (Keeling) Islands",
41407         "cc",
41408         "61",
41409         1
41410       ],
41411       [
41412         "Colombia",
41413         "co",
41414         "57"
41415       ],
41416       [
41417         "Comoros (‫جزر القمر‬‎)",
41418         "km",
41419         "269"
41420       ],
41421       [
41422         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41423         "cd",
41424         "243"
41425       ],
41426       [
41427         "Congo (Republic) (Congo-Brazzaville)",
41428         "cg",
41429         "242"
41430       ],
41431       [
41432         "Cook Islands",
41433         "ck",
41434         "682"
41435       ],
41436       [
41437         "Costa Rica",
41438         "cr",
41439         "506"
41440       ],
41441       [
41442         "Côte d’Ivoire",
41443         "ci",
41444         "225"
41445       ],
41446       [
41447         "Croatia (Hrvatska)",
41448         "hr",
41449         "385"
41450       ],
41451       [
41452         "Cuba",
41453         "cu",
41454         "53"
41455       ],
41456       [
41457         "Curaçao",
41458         "cw",
41459         "599",
41460         0
41461       ],
41462       [
41463         "Cyprus (Κύπρος)",
41464         "cy",
41465         "357"
41466       ],
41467       [
41468         "Czech Republic (Česká republika)",
41469         "cz",
41470         "420"
41471       ],
41472       [
41473         "Denmark (Danmark)",
41474         "dk",
41475         "45"
41476       ],
41477       [
41478         "Djibouti",
41479         "dj",
41480         "253"
41481       ],
41482       [
41483         "Dominica",
41484         "dm",
41485         "1767"
41486       ],
41487       [
41488         "Dominican Republic (República Dominicana)",
41489         "do",
41490         "1",
41491         2,
41492         ["809", "829", "849"]
41493       ],
41494       [
41495         "Ecuador",
41496         "ec",
41497         "593"
41498       ],
41499       [
41500         "Egypt (‫مصر‬‎)",
41501         "eg",
41502         "20"
41503       ],
41504       [
41505         "El Salvador",
41506         "sv",
41507         "503"
41508       ],
41509       [
41510         "Equatorial Guinea (Guinea Ecuatorial)",
41511         "gq",
41512         "240"
41513       ],
41514       [
41515         "Eritrea",
41516         "er",
41517         "291"
41518       ],
41519       [
41520         "Estonia (Eesti)",
41521         "ee",
41522         "372"
41523       ],
41524       [
41525         "Ethiopia",
41526         "et",
41527         "251"
41528       ],
41529       [
41530         "Falkland Islands (Islas Malvinas)",
41531         "fk",
41532         "500"
41533       ],
41534       [
41535         "Faroe Islands (Føroyar)",
41536         "fo",
41537         "298"
41538       ],
41539       [
41540         "Fiji",
41541         "fj",
41542         "679"
41543       ],
41544       [
41545         "Finland (Suomi)",
41546         "fi",
41547         "358",
41548         0
41549       ],
41550       [
41551         "France",
41552         "fr",
41553         "33"
41554       ],
41555       [
41556         "French Guiana (Guyane française)",
41557         "gf",
41558         "594"
41559       ],
41560       [
41561         "French Polynesia (Polynésie française)",
41562         "pf",
41563         "689"
41564       ],
41565       [
41566         "Gabon",
41567         "ga",
41568         "241"
41569       ],
41570       [
41571         "Gambia",
41572         "gm",
41573         "220"
41574       ],
41575       [
41576         "Georgia (საქართველო)",
41577         "ge",
41578         "995"
41579       ],
41580       [
41581         "Germany (Deutschland)",
41582         "de",
41583         "49"
41584       ],
41585       [
41586         "Ghana (Gaana)",
41587         "gh",
41588         "233"
41589       ],
41590       [
41591         "Gibraltar",
41592         "gi",
41593         "350"
41594       ],
41595       [
41596         "Greece (Ελλάδα)",
41597         "gr",
41598         "30"
41599       ],
41600       [
41601         "Greenland (Kalaallit Nunaat)",
41602         "gl",
41603         "299"
41604       ],
41605       [
41606         "Grenada",
41607         "gd",
41608         "1473"
41609       ],
41610       [
41611         "Guadeloupe",
41612         "gp",
41613         "590",
41614         0
41615       ],
41616       [
41617         "Guam",
41618         "gu",
41619         "1671"
41620       ],
41621       [
41622         "Guatemala",
41623         "gt",
41624         "502"
41625       ],
41626       [
41627         "Guernsey",
41628         "gg",
41629         "44",
41630         1
41631       ],
41632       [
41633         "Guinea (Guinée)",
41634         "gn",
41635         "224"
41636       ],
41637       [
41638         "Guinea-Bissau (Guiné Bissau)",
41639         "gw",
41640         "245"
41641       ],
41642       [
41643         "Guyana",
41644         "gy",
41645         "592"
41646       ],
41647       [
41648         "Haiti",
41649         "ht",
41650         "509"
41651       ],
41652       [
41653         "Honduras",
41654         "hn",
41655         "504"
41656       ],
41657       [
41658         "Hong Kong (香港)",
41659         "hk",
41660         "852"
41661       ],
41662       [
41663         "Hungary (Magyarország)",
41664         "hu",
41665         "36"
41666       ],
41667       [
41668         "Iceland (Ísland)",
41669         "is",
41670         "354"
41671       ],
41672       [
41673         "India (भारत)",
41674         "in",
41675         "91"
41676       ],
41677       [
41678         "Indonesia",
41679         "id",
41680         "62"
41681       ],
41682       [
41683         "Iran (‫ایران‬‎)",
41684         "ir",
41685         "98"
41686       ],
41687       [
41688         "Iraq (‫العراق‬‎)",
41689         "iq",
41690         "964"
41691       ],
41692       [
41693         "Ireland",
41694         "ie",
41695         "353"
41696       ],
41697       [
41698         "Isle of Man",
41699         "im",
41700         "44",
41701         2
41702       ],
41703       [
41704         "Israel (‫ישראל‬‎)",
41705         "il",
41706         "972"
41707       ],
41708       [
41709         "Italy (Italia)",
41710         "it",
41711         "39",
41712         0
41713       ],
41714       [
41715         "Jamaica",
41716         "jm",
41717         "1876"
41718       ],
41719       [
41720         "Japan (日本)",
41721         "jp",
41722         "81"
41723       ],
41724       [
41725         "Jersey",
41726         "je",
41727         "44",
41728         3
41729       ],
41730       [
41731         "Jordan (‫الأردن‬‎)",
41732         "jo",
41733         "962"
41734       ],
41735       [
41736         "Kazakhstan (Казахстан)",
41737         "kz",
41738         "7",
41739         1
41740       ],
41741       [
41742         "Kenya",
41743         "ke",
41744         "254"
41745       ],
41746       [
41747         "Kiribati",
41748         "ki",
41749         "686"
41750       ],
41751       [
41752         "Kosovo",
41753         "xk",
41754         "383"
41755       ],
41756       [
41757         "Kuwait (‫الكويت‬‎)",
41758         "kw",
41759         "965"
41760       ],
41761       [
41762         "Kyrgyzstan (Кыргызстан)",
41763         "kg",
41764         "996"
41765       ],
41766       [
41767         "Laos (ລາວ)",
41768         "la",
41769         "856"
41770       ],
41771       [
41772         "Latvia (Latvija)",
41773         "lv",
41774         "371"
41775       ],
41776       [
41777         "Lebanon (‫لبنان‬‎)",
41778         "lb",
41779         "961"
41780       ],
41781       [
41782         "Lesotho",
41783         "ls",
41784         "266"
41785       ],
41786       [
41787         "Liberia",
41788         "lr",
41789         "231"
41790       ],
41791       [
41792         "Libya (‫ليبيا‬‎)",
41793         "ly",
41794         "218"
41795       ],
41796       [
41797         "Liechtenstein",
41798         "li",
41799         "423"
41800       ],
41801       [
41802         "Lithuania (Lietuva)",
41803         "lt",
41804         "370"
41805       ],
41806       [
41807         "Luxembourg",
41808         "lu",
41809         "352"
41810       ],
41811       [
41812         "Macau (澳門)",
41813         "mo",
41814         "853"
41815       ],
41816       [
41817         "Macedonia (FYROM) (Македонија)",
41818         "mk",
41819         "389"
41820       ],
41821       [
41822         "Madagascar (Madagasikara)",
41823         "mg",
41824         "261"
41825       ],
41826       [
41827         "Malawi",
41828         "mw",
41829         "265"
41830       ],
41831       [
41832         "Malaysia",
41833         "my",
41834         "60"
41835       ],
41836       [
41837         "Maldives",
41838         "mv",
41839         "960"
41840       ],
41841       [
41842         "Mali",
41843         "ml",
41844         "223"
41845       ],
41846       [
41847         "Malta",
41848         "mt",
41849         "356"
41850       ],
41851       [
41852         "Marshall Islands",
41853         "mh",
41854         "692"
41855       ],
41856       [
41857         "Martinique",
41858         "mq",
41859         "596"
41860       ],
41861       [
41862         "Mauritania (‫موريتانيا‬‎)",
41863         "mr",
41864         "222"
41865       ],
41866       [
41867         "Mauritius (Moris)",
41868         "mu",
41869         "230"
41870       ],
41871       [
41872         "Mayotte",
41873         "yt",
41874         "262",
41875         1
41876       ],
41877       [
41878         "Mexico (México)",
41879         "mx",
41880         "52"
41881       ],
41882       [
41883         "Micronesia",
41884         "fm",
41885         "691"
41886       ],
41887       [
41888         "Moldova (Republica Moldova)",
41889         "md",
41890         "373"
41891       ],
41892       [
41893         "Monaco",
41894         "mc",
41895         "377"
41896       ],
41897       [
41898         "Mongolia (Монгол)",
41899         "mn",
41900         "976"
41901       ],
41902       [
41903         "Montenegro (Crna Gora)",
41904         "me",
41905         "382"
41906       ],
41907       [
41908         "Montserrat",
41909         "ms",
41910         "1664"
41911       ],
41912       [
41913         "Morocco (‫المغرب‬‎)",
41914         "ma",
41915         "212",
41916         0
41917       ],
41918       [
41919         "Mozambique (Moçambique)",
41920         "mz",
41921         "258"
41922       ],
41923       [
41924         "Myanmar (Burma) (မြန်မာ)",
41925         "mm",
41926         "95"
41927       ],
41928       [
41929         "Namibia (Namibië)",
41930         "na",
41931         "264"
41932       ],
41933       [
41934         "Nauru",
41935         "nr",
41936         "674"
41937       ],
41938       [
41939         "Nepal (नेपाल)",
41940         "np",
41941         "977"
41942       ],
41943       [
41944         "Netherlands (Nederland)",
41945         "nl",
41946         "31"
41947       ],
41948       [
41949         "New Caledonia (Nouvelle-Calédonie)",
41950         "nc",
41951         "687"
41952       ],
41953       [
41954         "New Zealand",
41955         "nz",
41956         "64"
41957       ],
41958       [
41959         "Nicaragua",
41960         "ni",
41961         "505"
41962       ],
41963       [
41964         "Niger (Nijar)",
41965         "ne",
41966         "227"
41967       ],
41968       [
41969         "Nigeria",
41970         "ng",
41971         "234"
41972       ],
41973       [
41974         "Niue",
41975         "nu",
41976         "683"
41977       ],
41978       [
41979         "Norfolk Island",
41980         "nf",
41981         "672"
41982       ],
41983       [
41984         "North Korea (조선 민주주의 인민 공화국)",
41985         "kp",
41986         "850"
41987       ],
41988       [
41989         "Northern Mariana Islands",
41990         "mp",
41991         "1670"
41992       ],
41993       [
41994         "Norway (Norge)",
41995         "no",
41996         "47",
41997         0
41998       ],
41999       [
42000         "Oman (‫عُمان‬‎)",
42001         "om",
42002         "968"
42003       ],
42004       [
42005         "Pakistan (‫پاکستان‬‎)",
42006         "pk",
42007         "92"
42008       ],
42009       [
42010         "Palau",
42011         "pw",
42012         "680"
42013       ],
42014       [
42015         "Palestine (‫فلسطين‬‎)",
42016         "ps",
42017         "970"
42018       ],
42019       [
42020         "Panama (Panamá)",
42021         "pa",
42022         "507"
42023       ],
42024       [
42025         "Papua New Guinea",
42026         "pg",
42027         "675"
42028       ],
42029       [
42030         "Paraguay",
42031         "py",
42032         "595"
42033       ],
42034       [
42035         "Peru (Perú)",
42036         "pe",
42037         "51"
42038       ],
42039       [
42040         "Philippines",
42041         "ph",
42042         "63"
42043       ],
42044       [
42045         "Poland (Polska)",
42046         "pl",
42047         "48"
42048       ],
42049       [
42050         "Portugal",
42051         "pt",
42052         "351"
42053       ],
42054       [
42055         "Puerto Rico",
42056         "pr",
42057         "1",
42058         3,
42059         ["787", "939"]
42060       ],
42061       [
42062         "Qatar (‫قطر‬‎)",
42063         "qa",
42064         "974"
42065       ],
42066       [
42067         "Réunion (La Réunion)",
42068         "re",
42069         "262",
42070         0
42071       ],
42072       [
42073         "Romania (România)",
42074         "ro",
42075         "40"
42076       ],
42077       [
42078         "Russia (Россия)",
42079         "ru",
42080         "7",
42081         0
42082       ],
42083       [
42084         "Rwanda",
42085         "rw",
42086         "250"
42087       ],
42088       [
42089         "Saint Barthélemy",
42090         "bl",
42091         "590",
42092         1
42093       ],
42094       [
42095         "Saint Helena",
42096         "sh",
42097         "290"
42098       ],
42099       [
42100         "Saint Kitts and Nevis",
42101         "kn",
42102         "1869"
42103       ],
42104       [
42105         "Saint Lucia",
42106         "lc",
42107         "1758"
42108       ],
42109       [
42110         "Saint Martin (Saint-Martin (partie française))",
42111         "mf",
42112         "590",
42113         2
42114       ],
42115       [
42116         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42117         "pm",
42118         "508"
42119       ],
42120       [
42121         "Saint Vincent and the Grenadines",
42122         "vc",
42123         "1784"
42124       ],
42125       [
42126         "Samoa",
42127         "ws",
42128         "685"
42129       ],
42130       [
42131         "San Marino",
42132         "sm",
42133         "378"
42134       ],
42135       [
42136         "São Tomé and Príncipe (São Tomé e Príncipe)",
42137         "st",
42138         "239"
42139       ],
42140       [
42141         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42142         "sa",
42143         "966"
42144       ],
42145       [
42146         "Senegal (Sénégal)",
42147         "sn",
42148         "221"
42149       ],
42150       [
42151         "Serbia (Србија)",
42152         "rs",
42153         "381"
42154       ],
42155       [
42156         "Seychelles",
42157         "sc",
42158         "248"
42159       ],
42160       [
42161         "Sierra Leone",
42162         "sl",
42163         "232"
42164       ],
42165       [
42166         "Singapore",
42167         "sg",
42168         "65"
42169       ],
42170       [
42171         "Sint Maarten",
42172         "sx",
42173         "1721"
42174       ],
42175       [
42176         "Slovakia (Slovensko)",
42177         "sk",
42178         "421"
42179       ],
42180       [
42181         "Slovenia (Slovenija)",
42182         "si",
42183         "386"
42184       ],
42185       [
42186         "Solomon Islands",
42187         "sb",
42188         "677"
42189       ],
42190       [
42191         "Somalia (Soomaaliya)",
42192         "so",
42193         "252"
42194       ],
42195       [
42196         "South Africa",
42197         "za",
42198         "27"
42199       ],
42200       [
42201         "South Korea (대한민국)",
42202         "kr",
42203         "82"
42204       ],
42205       [
42206         "South Sudan (‫جنوب السودان‬‎)",
42207         "ss",
42208         "211"
42209       ],
42210       [
42211         "Spain (España)",
42212         "es",
42213         "34"
42214       ],
42215       [
42216         "Sri Lanka (ශ්‍රී ලංකාව)",
42217         "lk",
42218         "94"
42219       ],
42220       [
42221         "Sudan (‫السودان‬‎)",
42222         "sd",
42223         "249"
42224       ],
42225       [
42226         "Suriname",
42227         "sr",
42228         "597"
42229       ],
42230       [
42231         "Svalbard and Jan Mayen",
42232         "sj",
42233         "47",
42234         1
42235       ],
42236       [
42237         "Swaziland",
42238         "sz",
42239         "268"
42240       ],
42241       [
42242         "Sweden (Sverige)",
42243         "se",
42244         "46"
42245       ],
42246       [
42247         "Switzerland (Schweiz)",
42248         "ch",
42249         "41"
42250       ],
42251       [
42252         "Syria (‫سوريا‬‎)",
42253         "sy",
42254         "963"
42255       ],
42256       [
42257         "Taiwan (台灣)",
42258         "tw",
42259         "886"
42260       ],
42261       [
42262         "Tajikistan",
42263         "tj",
42264         "992"
42265       ],
42266       [
42267         "Tanzania",
42268         "tz",
42269         "255"
42270       ],
42271       [
42272         "Thailand (ไทย)",
42273         "th",
42274         "66"
42275       ],
42276       [
42277         "Timor-Leste",
42278         "tl",
42279         "670"
42280       ],
42281       [
42282         "Togo",
42283         "tg",
42284         "228"
42285       ],
42286       [
42287         "Tokelau",
42288         "tk",
42289         "690"
42290       ],
42291       [
42292         "Tonga",
42293         "to",
42294         "676"
42295       ],
42296       [
42297         "Trinidad and Tobago",
42298         "tt",
42299         "1868"
42300       ],
42301       [
42302         "Tunisia (‫تونس‬‎)",
42303         "tn",
42304         "216"
42305       ],
42306       [
42307         "Turkey (Türkiye)",
42308         "tr",
42309         "90"
42310       ],
42311       [
42312         "Turkmenistan",
42313         "tm",
42314         "993"
42315       ],
42316       [
42317         "Turks and Caicos Islands",
42318         "tc",
42319         "1649"
42320       ],
42321       [
42322         "Tuvalu",
42323         "tv",
42324         "688"
42325       ],
42326       [
42327         "U.S. Virgin Islands",
42328         "vi",
42329         "1340"
42330       ],
42331       [
42332         "Uganda",
42333         "ug",
42334         "256"
42335       ],
42336       [
42337         "Ukraine (Україна)",
42338         "ua",
42339         "380"
42340       ],
42341       [
42342         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42343         "ae",
42344         "971"
42345       ],
42346       [
42347         "United Kingdom",
42348         "gb",
42349         "44",
42350         0
42351       ],
42352       [
42353         "United States",
42354         "us",
42355         "1",
42356         0
42357       ],
42358       [
42359         "Uruguay",
42360         "uy",
42361         "598"
42362       ],
42363       [
42364         "Uzbekistan (Oʻzbekiston)",
42365         "uz",
42366         "998"
42367       ],
42368       [
42369         "Vanuatu",
42370         "vu",
42371         "678"
42372       ],
42373       [
42374         "Vatican City (Città del Vaticano)",
42375         "va",
42376         "39",
42377         1
42378       ],
42379       [
42380         "Venezuela",
42381         "ve",
42382         "58"
42383       ],
42384       [
42385         "Vietnam (Việt Nam)",
42386         "vn",
42387         "84"
42388       ],
42389       [
42390         "Wallis and Futuna (Wallis-et-Futuna)",
42391         "wf",
42392         "681"
42393       ],
42394       [
42395         "Western Sahara (‫الصحراء الغربية‬‎)",
42396         "eh",
42397         "212",
42398         1
42399       ],
42400       [
42401         "Yemen (‫اليمن‬‎)",
42402         "ye",
42403         "967"
42404       ],
42405       [
42406         "Zambia",
42407         "zm",
42408         "260"
42409       ],
42410       [
42411         "Zimbabwe",
42412         "zw",
42413         "263"
42414       ],
42415       [
42416         "Åland Islands",
42417         "ax",
42418         "358",
42419         1
42420       ]
42421   ];
42422   
42423   return d;
42424 }/**
42425 *    This script refer to:
42426 *    Title: International Telephone Input
42427 *    Author: Jack O'Connor
42428 *    Code version:  v12.1.12
42429 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42430 **/
42431
42432 /**
42433  * @class Roo.bootstrap.PhoneInput
42434  * @extends Roo.bootstrap.TriggerField
42435  * An input with International dial-code selection
42436  
42437  * @cfg {String} defaultDialCode default '+852'
42438  * @cfg {Array} preferedCountries default []
42439   
42440  * @constructor
42441  * Create a new PhoneInput.
42442  * @param {Object} config Configuration options
42443  */
42444
42445 Roo.bootstrap.PhoneInput = function(config) {
42446     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42447 };
42448
42449 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42450         
42451         listWidth: undefined,
42452         
42453         selectedClass: 'active',
42454         
42455         invalidClass : "has-warning",
42456         
42457         validClass: 'has-success',
42458         
42459         allowed: '0123456789',
42460         
42461         max_length: 15,
42462         
42463         /**
42464          * @cfg {String} defaultDialCode The default dial code when initializing the input
42465          */
42466         defaultDialCode: '+852',
42467         
42468         /**
42469          * @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
42470          */
42471         preferedCountries: false,
42472         
42473         getAutoCreate : function()
42474         {
42475             var data = Roo.bootstrap.PhoneInputData();
42476             var align = this.labelAlign || this.parentLabelAlign();
42477             var id = Roo.id();
42478             
42479             this.allCountries = [];
42480             this.dialCodeMapping = [];
42481             
42482             for (var i = 0; i < data.length; i++) {
42483               var c = data[i];
42484               this.allCountries[i] = {
42485                 name: c[0],
42486                 iso2: c[1],
42487                 dialCode: c[2],
42488                 priority: c[3] || 0,
42489                 areaCodes: c[4] || null
42490               };
42491               this.dialCodeMapping[c[2]] = {
42492                   name: c[0],
42493                   iso2: c[1],
42494                   priority: c[3] || 0,
42495                   areaCodes: c[4] || null
42496               };
42497             }
42498             
42499             var cfg = {
42500                 cls: 'form-group',
42501                 cn: []
42502             };
42503             
42504             var input =  {
42505                 tag: 'input',
42506                 id : id,
42507                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42508                 maxlength: this.max_length,
42509                 cls : 'form-control tel-input',
42510                 autocomplete: 'new-password'
42511             };
42512             
42513             var hiddenInput = {
42514                 tag: 'input',
42515                 type: 'hidden',
42516                 cls: 'hidden-tel-input'
42517             };
42518             
42519             if (this.name) {
42520                 hiddenInput.name = this.name;
42521             }
42522             
42523             if (this.disabled) {
42524                 input.disabled = true;
42525             }
42526             
42527             var flag_container = {
42528                 tag: 'div',
42529                 cls: 'flag-box',
42530                 cn: [
42531                     {
42532                         tag: 'div',
42533                         cls: 'flag'
42534                     },
42535                     {
42536                         tag: 'div',
42537                         cls: 'caret'
42538                     }
42539                 ]
42540             };
42541             
42542             var box = {
42543                 tag: 'div',
42544                 cls: this.hasFeedback ? 'has-feedback' : '',
42545                 cn: [
42546                     hiddenInput,
42547                     input,
42548                     {
42549                         tag: 'input',
42550                         cls: 'dial-code-holder',
42551                         disabled: true
42552                     }
42553                 ]
42554             };
42555             
42556             var container = {
42557                 cls: 'roo-select2-container input-group',
42558                 cn: [
42559                     flag_container,
42560                     box
42561                 ]
42562             };
42563             
42564             if (this.fieldLabel.length) {
42565                 var indicator = {
42566                     tag: 'i',
42567                     tooltip: 'This field is required'
42568                 };
42569                 
42570                 var label = {
42571                     tag: 'label',
42572                     'for':  id,
42573                     cls: 'control-label',
42574                     cn: []
42575                 };
42576                 
42577                 var label_text = {
42578                     tag: 'span',
42579                     html: this.fieldLabel
42580                 };
42581                 
42582                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42583                 label.cn = [
42584                     indicator,
42585                     label_text
42586                 ];
42587                 
42588                 if(this.indicatorpos == 'right') {
42589                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42590                     label.cn = [
42591                         label_text,
42592                         indicator
42593                     ];
42594                 }
42595                 
42596                 if(align == 'left') {
42597                     container = {
42598                         tag: 'div',
42599                         cn: [
42600                             container
42601                         ]
42602                     };
42603                     
42604                     if(this.labelWidth > 12){
42605                         label.style = "width: " + this.labelWidth + 'px';
42606                     }
42607                     if(this.labelWidth < 13 && this.labelmd == 0){
42608                         this.labelmd = this.labelWidth;
42609                     }
42610                     if(this.labellg > 0){
42611                         label.cls += ' col-lg-' + this.labellg;
42612                         input.cls += ' col-lg-' + (12 - this.labellg);
42613                     }
42614                     if(this.labelmd > 0){
42615                         label.cls += ' col-md-' + this.labelmd;
42616                         container.cls += ' col-md-' + (12 - this.labelmd);
42617                     }
42618                     if(this.labelsm > 0){
42619                         label.cls += ' col-sm-' + this.labelsm;
42620                         container.cls += ' col-sm-' + (12 - this.labelsm);
42621                     }
42622                     if(this.labelxs > 0){
42623                         label.cls += ' col-xs-' + this.labelxs;
42624                         container.cls += ' col-xs-' + (12 - this.labelxs);
42625                     }
42626                 }
42627             }
42628             
42629             cfg.cn = [
42630                 label,
42631                 container
42632             ];
42633             
42634             var settings = this;
42635             
42636             ['xs','sm','md','lg'].map(function(size){
42637                 if (settings[size]) {
42638                     cfg.cls += ' col-' + size + '-' + settings[size];
42639                 }
42640             });
42641             
42642             this.store = new Roo.data.Store({
42643                 proxy : new Roo.data.MemoryProxy({}),
42644                 reader : new Roo.data.JsonReader({
42645                     fields : [
42646                         {
42647                             'name' : 'name',
42648                             'type' : 'string'
42649                         },
42650                         {
42651                             'name' : 'iso2',
42652                             'type' : 'string'
42653                         },
42654                         {
42655                             'name' : 'dialCode',
42656                             'type' : 'string'
42657                         },
42658                         {
42659                             'name' : 'priority',
42660                             'type' : 'string'
42661                         },
42662                         {
42663                             'name' : 'areaCodes',
42664                             'type' : 'string'
42665                         }
42666                     ]
42667                 })
42668             });
42669             
42670             if(!this.preferedCountries) {
42671                 this.preferedCountries = [
42672                     'hk',
42673                     'gb',
42674                     'us'
42675                 ];
42676             }
42677             
42678             var p = this.preferedCountries.reverse();
42679             
42680             if(p) {
42681                 for (var i = 0; i < p.length; i++) {
42682                     for (var j = 0; j < this.allCountries.length; j++) {
42683                         if(this.allCountries[j].iso2 == p[i]) {
42684                             var t = this.allCountries[j];
42685                             this.allCountries.splice(j,1);
42686                             this.allCountries.unshift(t);
42687                         }
42688                     } 
42689                 }
42690             }
42691             
42692             this.store.proxy.data = {
42693                 success: true,
42694                 data: this.allCountries
42695             };
42696             
42697             return cfg;
42698         },
42699         
42700         initEvents : function()
42701         {
42702             this.createList();
42703             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42704             
42705             this.indicator = this.indicatorEl();
42706             this.flag = this.flagEl();
42707             this.dialCodeHolder = this.dialCodeHolderEl();
42708             
42709             this.trigger = this.el.select('div.flag-box',true).first();
42710             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42711             
42712             var _this = this;
42713             
42714             (function(){
42715                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42716                 _this.list.setWidth(lw);
42717             }).defer(100);
42718             
42719             this.list.on('mouseover', this.onViewOver, this);
42720             this.list.on('mousemove', this.onViewMove, this);
42721             this.inputEl().on("keyup", this.onKeyUp, this);
42722             this.inputEl().on("keypress", this.onKeyPress, this);
42723             
42724             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42725
42726             this.view = new Roo.View(this.list, this.tpl, {
42727                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42728             });
42729             
42730             this.view.on('click', this.onViewClick, this);
42731             this.setValue(this.defaultDialCode);
42732         },
42733         
42734         onTriggerClick : function(e)
42735         {
42736             Roo.log('trigger click');
42737             if(this.disabled){
42738                 return;
42739             }
42740             
42741             if(this.isExpanded()){
42742                 this.collapse();
42743                 this.hasFocus = false;
42744             }else {
42745                 this.store.load({});
42746                 this.hasFocus = true;
42747                 this.expand();
42748             }
42749         },
42750         
42751         isExpanded : function()
42752         {
42753             return this.list.isVisible();
42754         },
42755         
42756         collapse : function()
42757         {
42758             if(!this.isExpanded()){
42759                 return;
42760             }
42761             this.list.hide();
42762             Roo.get(document).un('mousedown', this.collapseIf, this);
42763             Roo.get(document).un('mousewheel', this.collapseIf, this);
42764             this.fireEvent('collapse', this);
42765             this.validate();
42766         },
42767         
42768         expand : function()
42769         {
42770             Roo.log('expand');
42771
42772             if(this.isExpanded() || !this.hasFocus){
42773                 return;
42774             }
42775             
42776             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42777             this.list.setWidth(lw);
42778             
42779             this.list.show();
42780             this.restrictHeight();
42781             
42782             Roo.get(document).on('mousedown', this.collapseIf, this);
42783             Roo.get(document).on('mousewheel', this.collapseIf, this);
42784             
42785             this.fireEvent('expand', this);
42786         },
42787         
42788         restrictHeight : function()
42789         {
42790             this.list.alignTo(this.inputEl(), this.listAlign);
42791             this.list.alignTo(this.inputEl(), this.listAlign);
42792         },
42793         
42794         onViewOver : function(e, t)
42795         {
42796             if(this.inKeyMode){
42797                 return;
42798             }
42799             var item = this.view.findItemFromChild(t);
42800             
42801             if(item){
42802                 var index = this.view.indexOf(item);
42803                 this.select(index, false);
42804             }
42805         },
42806
42807         // private
42808         onViewClick : function(view, doFocus, el, e)
42809         {
42810             var index = this.view.getSelectedIndexes()[0];
42811             
42812             var r = this.store.getAt(index);
42813             
42814             if(r){
42815                 this.onSelect(r, index);
42816             }
42817             if(doFocus !== false && !this.blockFocus){
42818                 this.inputEl().focus();
42819             }
42820         },
42821         
42822         onViewMove : function(e, t)
42823         {
42824             this.inKeyMode = false;
42825         },
42826         
42827         select : function(index, scrollIntoView)
42828         {
42829             this.selectedIndex = index;
42830             this.view.select(index);
42831             if(scrollIntoView !== false){
42832                 var el = this.view.getNode(index);
42833                 if(el){
42834                     this.list.scrollChildIntoView(el, false);
42835                 }
42836             }
42837         },
42838         
42839         createList : function()
42840         {
42841             this.list = Roo.get(document.body).createChild({
42842                 tag: 'ul',
42843                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42844                 style: 'display:none'
42845             });
42846             
42847             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42848         },
42849         
42850         collapseIf : function(e)
42851         {
42852             var in_combo  = e.within(this.el);
42853             var in_list =  e.within(this.list);
42854             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42855             
42856             if (in_combo || in_list || is_list) {
42857                 return;
42858             }
42859             this.collapse();
42860         },
42861         
42862         onSelect : function(record, index)
42863         {
42864             if(this.fireEvent('beforeselect', this, record, index) !== false){
42865                 
42866                 this.setFlagClass(record.data.iso2);
42867                 this.setDialCode(record.data.dialCode);
42868                 this.hasFocus = false;
42869                 this.collapse();
42870                 this.fireEvent('select', this, record, index);
42871             }
42872         },
42873         
42874         flagEl : function()
42875         {
42876             var flag = this.el.select('div.flag',true).first();
42877             if(!flag){
42878                 return false;
42879             }
42880             return flag;
42881         },
42882         
42883         dialCodeHolderEl : function()
42884         {
42885             var d = this.el.select('input.dial-code-holder',true).first();
42886             if(!d){
42887                 return false;
42888             }
42889             return d;
42890         },
42891         
42892         setDialCode : function(v)
42893         {
42894             this.dialCodeHolder.dom.value = '+'+v;
42895         },
42896         
42897         setFlagClass : function(n)
42898         {
42899             this.flag.dom.className = 'flag '+n;
42900         },
42901         
42902         getValue : function()
42903         {
42904             var v = this.inputEl().getValue();
42905             if(this.dialCodeHolder) {
42906                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42907             }
42908             return v;
42909         },
42910         
42911         setValue : function(v)
42912         {
42913             var d = this.getDialCode(v);
42914             
42915             //invalid dial code
42916             if(v.length == 0 || !d || d.length == 0) {
42917                 if(this.rendered){
42918                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42919                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42920                 }
42921                 return;
42922             }
42923             
42924             //valid dial code
42925             this.setFlagClass(this.dialCodeMapping[d].iso2);
42926             this.setDialCode(d);
42927             this.inputEl().dom.value = v.replace('+'+d,'');
42928             this.hiddenEl().dom.value = this.getValue();
42929             
42930             this.validate();
42931         },
42932         
42933         getDialCode : function(v)
42934         {
42935             v = v ||  '';
42936             
42937             if (v.length == 0) {
42938                 return this.dialCodeHolder.dom.value;
42939             }
42940             
42941             var dialCode = "";
42942             if (v.charAt(0) != "+") {
42943                 return false;
42944             }
42945             var numericChars = "";
42946             for (var i = 1; i < v.length; i++) {
42947               var c = v.charAt(i);
42948               if (!isNaN(c)) {
42949                 numericChars += c;
42950                 if (this.dialCodeMapping[numericChars]) {
42951                   dialCode = v.substr(1, i);
42952                 }
42953                 if (numericChars.length == 4) {
42954                   break;
42955                 }
42956               }
42957             }
42958             return dialCode;
42959         },
42960         
42961         reset : function()
42962         {
42963             this.setValue(this.defaultDialCode);
42964             this.validate();
42965         },
42966         
42967         hiddenEl : function()
42968         {
42969             return this.el.select('input.hidden-tel-input',true).first();
42970         },
42971         
42972         // after setting val
42973         onKeyUp : function(e){
42974             this.setValue(this.getValue());
42975         },
42976         
42977         onKeyPress : function(e){
42978             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42979                 e.stopEvent();
42980             }
42981         }
42982         
42983 });
42984 /**
42985  * @class Roo.bootstrap.MoneyField
42986  * @extends Roo.bootstrap.ComboBox
42987  * Bootstrap MoneyField class
42988  * 
42989  * @constructor
42990  * Create a new MoneyField.
42991  * @param {Object} config Configuration options
42992  */
42993
42994 Roo.bootstrap.MoneyField = function(config) {
42995     
42996     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42997     
42998 };
42999
43000 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
43001     
43002     /**
43003      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43004      */
43005     allowDecimals : true,
43006     /**
43007      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43008      */
43009     decimalSeparator : ".",
43010     /**
43011      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43012      */
43013     decimalPrecision : 0,
43014     /**
43015      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43016      */
43017     allowNegative : true,
43018     /**
43019      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43020      */
43021     allowZero: true,
43022     /**
43023      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43024      */
43025     minValue : Number.NEGATIVE_INFINITY,
43026     /**
43027      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43028      */
43029     maxValue : Number.MAX_VALUE,
43030     /**
43031      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43032      */
43033     minText : "The minimum value for this field is {0}",
43034     /**
43035      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43036      */
43037     maxText : "The maximum value for this field is {0}",
43038     /**
43039      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43040      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43041      */
43042     nanText : "{0} is not a valid number",
43043     /**
43044      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43045      */
43046     castInt : true,
43047     /**
43048      * @cfg {String} defaults currency of the MoneyField
43049      * value should be in lkey
43050      */
43051     defaultCurrency : false,
43052     /**
43053      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43054      */
43055     thousandsDelimiter : false,
43056     /**
43057      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43058      */
43059     max_length: false,
43060     
43061     inputlg : 9,
43062     inputmd : 9,
43063     inputsm : 9,
43064     inputxs : 6,
43065     
43066     store : false,
43067     
43068     getAutoCreate : function()
43069     {
43070         var align = this.labelAlign || this.parentLabelAlign();
43071         
43072         var id = Roo.id();
43073
43074         var cfg = {
43075             cls: 'form-group',
43076             cn: []
43077         };
43078
43079         var input =  {
43080             tag: 'input',
43081             id : id,
43082             cls : 'form-control roo-money-amount-input',
43083             autocomplete: 'new-password'
43084         };
43085         
43086         var hiddenInput = {
43087             tag: 'input',
43088             type: 'hidden',
43089             id: Roo.id(),
43090             cls: 'hidden-number-input'
43091         };
43092         
43093         if(this.max_length) {
43094             input.maxlength = this.max_length; 
43095         }
43096         
43097         if (this.name) {
43098             hiddenInput.name = this.name;
43099         }
43100
43101         if (this.disabled) {
43102             input.disabled = true;
43103         }
43104
43105         var clg = 12 - this.inputlg;
43106         var cmd = 12 - this.inputmd;
43107         var csm = 12 - this.inputsm;
43108         var cxs = 12 - this.inputxs;
43109         
43110         var container = {
43111             tag : 'div',
43112             cls : 'row roo-money-field',
43113             cn : [
43114                 {
43115                     tag : 'div',
43116                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43117                     cn : [
43118                         {
43119                             tag : 'div',
43120                             cls: 'roo-select2-container input-group',
43121                             cn: [
43122                                 {
43123                                     tag : 'input',
43124                                     cls : 'form-control roo-money-currency-input',
43125                                     autocomplete: 'new-password',
43126                                     readOnly : 1,
43127                                     name : this.currencyName
43128                                 },
43129                                 {
43130                                     tag :'span',
43131                                     cls : 'input-group-addon',
43132                                     cn : [
43133                                         {
43134                                             tag: 'span',
43135                                             cls: 'caret'
43136                                         }
43137                                     ]
43138                                 }
43139                             ]
43140                         }
43141                     ]
43142                 },
43143                 {
43144                     tag : 'div',
43145                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43146                     cn : [
43147                         {
43148                             tag: 'div',
43149                             cls: this.hasFeedback ? 'has-feedback' : '',
43150                             cn: [
43151                                 input
43152                             ]
43153                         }
43154                     ]
43155                 }
43156             ]
43157             
43158         };
43159         
43160         if (this.fieldLabel.length) {
43161             var indicator = {
43162                 tag: 'i',
43163                 tooltip: 'This field is required'
43164             };
43165
43166             var label = {
43167                 tag: 'label',
43168                 'for':  id,
43169                 cls: 'control-label',
43170                 cn: []
43171             };
43172
43173             var label_text = {
43174                 tag: 'span',
43175                 html: this.fieldLabel
43176             };
43177
43178             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43179             label.cn = [
43180                 indicator,
43181                 label_text
43182             ];
43183
43184             if(this.indicatorpos == 'right') {
43185                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43186                 label.cn = [
43187                     label_text,
43188                     indicator
43189                 ];
43190             }
43191
43192             if(align == 'left') {
43193                 container = {
43194                     tag: 'div',
43195                     cn: [
43196                         container
43197                     ]
43198                 };
43199
43200                 if(this.labelWidth > 12){
43201                     label.style = "width: " + this.labelWidth + 'px';
43202                 }
43203                 if(this.labelWidth < 13 && this.labelmd == 0){
43204                     this.labelmd = this.labelWidth;
43205                 }
43206                 if(this.labellg > 0){
43207                     label.cls += ' col-lg-' + this.labellg;
43208                     input.cls += ' col-lg-' + (12 - this.labellg);
43209                 }
43210                 if(this.labelmd > 0){
43211                     label.cls += ' col-md-' + this.labelmd;
43212                     container.cls += ' col-md-' + (12 - this.labelmd);
43213                 }
43214                 if(this.labelsm > 0){
43215                     label.cls += ' col-sm-' + this.labelsm;
43216                     container.cls += ' col-sm-' + (12 - this.labelsm);
43217                 }
43218                 if(this.labelxs > 0){
43219                     label.cls += ' col-xs-' + this.labelxs;
43220                     container.cls += ' col-xs-' + (12 - this.labelxs);
43221                 }
43222             }
43223         }
43224
43225         cfg.cn = [
43226             label,
43227             container,
43228             hiddenInput
43229         ];
43230         
43231         var settings = this;
43232
43233         ['xs','sm','md','lg'].map(function(size){
43234             if (settings[size]) {
43235                 cfg.cls += ' col-' + size + '-' + settings[size];
43236             }
43237         });
43238         
43239         return cfg;
43240     },
43241     
43242     initEvents : function()
43243     {
43244         this.indicator = this.indicatorEl();
43245         
43246         this.initCurrencyEvent();
43247         
43248         this.initNumberEvent();
43249     },
43250     
43251     initCurrencyEvent : function()
43252     {
43253         if (!this.store) {
43254             throw "can not find store for combo";
43255         }
43256         
43257         this.store = Roo.factory(this.store, Roo.data);
43258         this.store.parent = this;
43259         
43260         this.createList();
43261         
43262         this.triggerEl = this.el.select('.input-group-addon', true).first();
43263         
43264         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43265         
43266         var _this = this;
43267         
43268         (function(){
43269             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43270             _this.list.setWidth(lw);
43271         }).defer(100);
43272         
43273         this.list.on('mouseover', this.onViewOver, this);
43274         this.list.on('mousemove', this.onViewMove, this);
43275         this.list.on('scroll', this.onViewScroll, this);
43276         
43277         if(!this.tpl){
43278             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43279         }
43280         
43281         this.view = new Roo.View(this.list, this.tpl, {
43282             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43283         });
43284         
43285         this.view.on('click', this.onViewClick, this);
43286         
43287         this.store.on('beforeload', this.onBeforeLoad, this);
43288         this.store.on('load', this.onLoad, this);
43289         this.store.on('loadexception', this.onLoadException, this);
43290         
43291         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43292             "up" : function(e){
43293                 this.inKeyMode = true;
43294                 this.selectPrev();
43295             },
43296
43297             "down" : function(e){
43298                 if(!this.isExpanded()){
43299                     this.onTriggerClick();
43300                 }else{
43301                     this.inKeyMode = true;
43302                     this.selectNext();
43303                 }
43304             },
43305
43306             "enter" : function(e){
43307                 this.collapse();
43308                 
43309                 if(this.fireEvent("specialkey", this, e)){
43310                     this.onViewClick(false);
43311                 }
43312                 
43313                 return true;
43314             },
43315
43316             "esc" : function(e){
43317                 this.collapse();
43318             },
43319
43320             "tab" : function(e){
43321                 this.collapse();
43322                 
43323                 if(this.fireEvent("specialkey", this, e)){
43324                     this.onViewClick(false);
43325                 }
43326                 
43327                 return true;
43328             },
43329
43330             scope : this,
43331
43332             doRelay : function(foo, bar, hname){
43333                 if(hname == 'down' || this.scope.isExpanded()){
43334                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43335                 }
43336                 return true;
43337             },
43338
43339             forceKeyDown: true
43340         });
43341         
43342         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43343         
43344     },
43345     
43346     initNumberEvent : function(e)
43347     {
43348         this.inputEl().on("keydown" , this.fireKey,  this);
43349         this.inputEl().on("focus", this.onFocus,  this);
43350         this.inputEl().on("blur", this.onBlur,  this);
43351         
43352         this.inputEl().relayEvent('keyup', this);
43353         
43354         if(this.indicator){
43355             this.indicator.addClass('invisible');
43356         }
43357  
43358         this.originalValue = this.getValue();
43359         
43360         if(this.validationEvent == 'keyup'){
43361             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43362             this.inputEl().on('keyup', this.filterValidation, this);
43363         }
43364         else if(this.validationEvent !== false){
43365             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43366         }
43367         
43368         if(this.selectOnFocus){
43369             this.on("focus", this.preFocus, this);
43370             
43371         }
43372         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43373             this.inputEl().on("keypress", this.filterKeys, this);
43374         } else {
43375             this.inputEl().relayEvent('keypress', this);
43376         }
43377         
43378         var allowed = "0123456789";
43379         
43380         if(this.allowDecimals){
43381             allowed += this.decimalSeparator;
43382         }
43383         
43384         if(this.allowNegative){
43385             allowed += "-";
43386         }
43387         
43388         if(this.thousandsDelimiter) {
43389             allowed += ",";
43390         }
43391         
43392         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43393         
43394         var keyPress = function(e){
43395             
43396             var k = e.getKey();
43397             
43398             var c = e.getCharCode();
43399             
43400             if(
43401                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43402                     allowed.indexOf(String.fromCharCode(c)) === -1
43403             ){
43404                 e.stopEvent();
43405                 return;
43406             }
43407             
43408             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43409                 return;
43410             }
43411             
43412             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43413                 e.stopEvent();
43414             }
43415         };
43416         
43417         this.inputEl().on("keypress", keyPress, this);
43418         
43419     },
43420     
43421     onTriggerClick : function(e)
43422     {   
43423         if(this.disabled){
43424             return;
43425         }
43426         
43427         this.page = 0;
43428         this.loadNext = false;
43429         
43430         if(this.isExpanded()){
43431             this.collapse();
43432             return;
43433         }
43434         
43435         this.hasFocus = true;
43436         
43437         if(this.triggerAction == 'all') {
43438             this.doQuery(this.allQuery, true);
43439             return;
43440         }
43441         
43442         this.doQuery(this.getRawValue());
43443     },
43444     
43445     getCurrency : function()
43446     {   
43447         var v = this.currencyEl().getValue();
43448         
43449         return v;
43450     },
43451     
43452     restrictHeight : function()
43453     {
43454         this.list.alignTo(this.currencyEl(), this.listAlign);
43455         this.list.alignTo(this.currencyEl(), this.listAlign);
43456     },
43457     
43458     onViewClick : function(view, doFocus, el, e)
43459     {
43460         var index = this.view.getSelectedIndexes()[0];
43461         
43462         var r = this.store.getAt(index);
43463         
43464         if(r){
43465             this.onSelect(r, index);
43466         }
43467     },
43468     
43469     onSelect : function(record, index){
43470         
43471         if(this.fireEvent('beforeselect', this, record, index) !== false){
43472         
43473             this.setFromCurrencyData(index > -1 ? record.data : false);
43474             
43475             this.collapse();
43476             
43477             this.fireEvent('select', this, record, index);
43478         }
43479     },
43480     
43481     setFromCurrencyData : function(o)
43482     {
43483         var currency = '';
43484         
43485         this.lastCurrency = o;
43486         
43487         if (this.currencyField) {
43488             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43489         } else {
43490             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43491         }
43492         
43493         this.lastSelectionText = currency;
43494         
43495         //setting default currency
43496         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43497             this.setCurrency(this.defaultCurrency);
43498             return;
43499         }
43500         
43501         this.setCurrency(currency);
43502     },
43503     
43504     setFromData : function(o)
43505     {
43506         var c = {};
43507         
43508         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43509         
43510         this.setFromCurrencyData(c);
43511         
43512         var value = '';
43513         
43514         if (this.name) {
43515             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43516         } else {
43517             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43518         }
43519         
43520         this.setValue(value);
43521         
43522     },
43523     
43524     setCurrency : function(v)
43525     {   
43526         this.currencyValue = v;
43527         
43528         if(this.rendered){
43529             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43530             this.validate();
43531         }
43532     },
43533     
43534     setValue : function(v)
43535     {
43536         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43537         
43538         this.value = v;
43539         
43540         if(this.rendered){
43541             
43542             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43543             
43544             this.inputEl().dom.value = (v == '') ? '' :
43545                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43546             
43547             if(!this.allowZero && v === '0') {
43548                 this.hiddenEl().dom.value = '';
43549                 this.inputEl().dom.value = '';
43550             }
43551             
43552             this.validate();
43553         }
43554     },
43555     
43556     getRawValue : function()
43557     {
43558         var v = this.inputEl().getValue();
43559         
43560         return v;
43561     },
43562     
43563     getValue : function()
43564     {
43565         return this.fixPrecision(this.parseValue(this.getRawValue()));
43566     },
43567     
43568     parseValue : function(value)
43569     {
43570         if(this.thousandsDelimiter) {
43571             value += "";
43572             r = new RegExp(",", "g");
43573             value = value.replace(r, "");
43574         }
43575         
43576         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43577         return isNaN(value) ? '' : value;
43578         
43579     },
43580     
43581     fixPrecision : function(value)
43582     {
43583         if(this.thousandsDelimiter) {
43584             value += "";
43585             r = new RegExp(",", "g");
43586             value = value.replace(r, "");
43587         }
43588         
43589         var nan = isNaN(value);
43590         
43591         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43592             return nan ? '' : value;
43593         }
43594         return parseFloat(value).toFixed(this.decimalPrecision);
43595     },
43596     
43597     decimalPrecisionFcn : function(v)
43598     {
43599         return Math.floor(v);
43600     },
43601     
43602     validateValue : function(value)
43603     {
43604         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43605             return false;
43606         }
43607         
43608         var num = this.parseValue(value);
43609         
43610         if(isNaN(num)){
43611             this.markInvalid(String.format(this.nanText, value));
43612             return false;
43613         }
43614         
43615         if(num < this.minValue){
43616             this.markInvalid(String.format(this.minText, this.minValue));
43617             return false;
43618         }
43619         
43620         if(num > this.maxValue){
43621             this.markInvalid(String.format(this.maxText, this.maxValue));
43622             return false;
43623         }
43624         
43625         return true;
43626     },
43627     
43628     validate : function()
43629     {
43630         if(this.disabled || this.allowBlank){
43631             this.markValid();
43632             return true;
43633         }
43634         
43635         var currency = this.getCurrency();
43636         
43637         if(this.validateValue(this.getRawValue()) && currency.length){
43638             this.markValid();
43639             return true;
43640         }
43641         
43642         this.markInvalid();
43643         return false;
43644     },
43645     
43646     getName: function()
43647     {
43648         return this.name;
43649     },
43650     
43651     beforeBlur : function()
43652     {
43653         if(!this.castInt){
43654             return;
43655         }
43656         
43657         var v = this.parseValue(this.getRawValue());
43658         
43659         if(v || v == 0){
43660             this.setValue(v);
43661         }
43662     },
43663     
43664     onBlur : function()
43665     {
43666         this.beforeBlur();
43667         
43668         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43669             //this.el.removeClass(this.focusClass);
43670         }
43671         
43672         this.hasFocus = false;
43673         
43674         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43675             this.validate();
43676         }
43677         
43678         var v = this.getValue();
43679         
43680         if(String(v) !== String(this.startValue)){
43681             this.fireEvent('change', this, v, this.startValue);
43682         }
43683         
43684         this.fireEvent("blur", this);
43685     },
43686     
43687     inputEl : function()
43688     {
43689         return this.el.select('.roo-money-amount-input', true).first();
43690     },
43691     
43692     currencyEl : function()
43693     {
43694         return this.el.select('.roo-money-currency-input', true).first();
43695     },
43696     
43697     hiddenEl : function()
43698     {
43699         return this.el.select('input.hidden-number-input',true).first();
43700     }
43701     
43702 });/**
43703  * @class Roo.bootstrap.BezierSignature
43704  * @extends Roo.bootstrap.Component
43705  * Bootstrap BezierSignature class
43706  * This script refer to:
43707  *    Title: Signature Pad
43708  *    Author: szimek
43709  *    Availability: https://github.com/szimek/signature_pad
43710  *
43711  * @constructor
43712  * Create a new BezierSignature
43713  * @param {Object} config The config object
43714  */
43715
43716 Roo.bootstrap.BezierSignature = function(config){
43717     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43718     this.addEvents({
43719         "resize" : true
43720     });
43721 };
43722
43723 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43724 {
43725      
43726     curve_data: [],
43727     
43728     is_empty: true,
43729     
43730     mouse_btn_down: true,
43731     
43732     /**
43733      * @cfg {int} canvas height
43734      */
43735     canvas_height: '200px',
43736     
43737     /**
43738      * @cfg {float|function} Radius of a single dot.
43739      */ 
43740     dot_size: false,
43741     
43742     /**
43743      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43744      */
43745     min_width: 0.5,
43746     
43747     /**
43748      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43749      */
43750     max_width: 2.5,
43751     
43752     /**
43753      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43754      */
43755     throttle: 16,
43756     
43757     /**
43758      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43759      */
43760     min_distance: 5,
43761     
43762     /**
43763      * @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.
43764      */
43765     bg_color: 'rgba(0, 0, 0, 0)',
43766     
43767     /**
43768      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43769      */
43770     dot_color: 'black',
43771     
43772     /**
43773      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43774      */ 
43775     velocity_filter_weight: 0.7,
43776     
43777     /**
43778      * @cfg {function} Callback when stroke begin. 
43779      */
43780     onBegin: false,
43781     
43782     /**
43783      * @cfg {function} Callback when stroke end.
43784      */
43785     onEnd: false,
43786     
43787     getAutoCreate : function()
43788     {
43789         var cls = 'roo-signature column';
43790         
43791         if(this.cls){
43792             cls += ' ' + this.cls;
43793         }
43794         
43795         var col_sizes = [
43796             'lg',
43797             'md',
43798             'sm',
43799             'xs'
43800         ];
43801         
43802         for(var i = 0; i < col_sizes.length; i++) {
43803             if(this[col_sizes[i]]) {
43804                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43805             }
43806         }
43807         
43808         var cfg = {
43809             tag: 'div',
43810             cls: cls,
43811             cn: [
43812                 {
43813                     tag: 'div',
43814                     cls: 'roo-signature-body',
43815                     cn: [
43816                         {
43817                             tag: 'canvas',
43818                             cls: 'roo-signature-body-canvas',
43819                             height: this.canvas_height,
43820                             width: this.canvas_width
43821                         }
43822                     ]
43823                 },
43824                 {
43825                     tag: 'input',
43826                     type: 'file',
43827                     style: 'display: none'
43828                 }
43829             ]
43830         };
43831         
43832         return cfg;
43833     },
43834     
43835     initEvents: function() 
43836     {
43837         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43838         
43839         var canvas = this.canvasEl();
43840         
43841         // mouse && touch event swapping...
43842         canvas.dom.style.touchAction = 'none';
43843         canvas.dom.style.msTouchAction = 'none';
43844         
43845         this.mouse_btn_down = false;
43846         canvas.on('mousedown', this._handleMouseDown, this);
43847         canvas.on('mousemove', this._handleMouseMove, this);
43848         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43849         
43850         if (window.PointerEvent) {
43851             canvas.on('pointerdown', this._handleMouseDown, this);
43852             canvas.on('pointermove', this._handleMouseMove, this);
43853             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43854         }
43855         
43856         if ('ontouchstart' in window) {
43857             canvas.on('touchstart', this._handleTouchStart, this);
43858             canvas.on('touchmove', this._handleTouchMove, this);
43859             canvas.on('touchend', this._handleTouchEnd, this);
43860         }
43861         
43862         Roo.EventManager.onWindowResize(this.resize, this, true);
43863         
43864         // file input event
43865         this.fileEl().on('change', this.uploadImage, this);
43866         
43867         this.clear();
43868         
43869         this.resize();
43870     },
43871     
43872     resize: function(){
43873         
43874         var canvas = this.canvasEl().dom;
43875         var ctx = this.canvasElCtx();
43876         var img_data = false;
43877         
43878         if(canvas.width > 0) {
43879             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43880         }
43881         // setting canvas width will clean img data
43882         canvas.width = 0;
43883         
43884         var style = window.getComputedStyle ? 
43885             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43886             
43887         var padding_left = parseInt(style.paddingLeft) || 0;
43888         var padding_right = parseInt(style.paddingRight) || 0;
43889         
43890         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43891         
43892         if(img_data) {
43893             ctx.putImageData(img_data, 0, 0);
43894         }
43895     },
43896     
43897     _handleMouseDown: function(e)
43898     {
43899         if (e.browserEvent.which === 1) {
43900             this.mouse_btn_down = true;
43901             this.strokeBegin(e);
43902         }
43903     },
43904     
43905     _handleMouseMove: function (e)
43906     {
43907         if (this.mouse_btn_down) {
43908             this.strokeMoveUpdate(e);
43909         }
43910     },
43911     
43912     _handleMouseUp: function (e)
43913     {
43914         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43915             this.mouse_btn_down = false;
43916             this.strokeEnd(e);
43917         }
43918     },
43919     
43920     _handleTouchStart: function (e) {
43921         
43922         e.preventDefault();
43923         if (e.browserEvent.targetTouches.length === 1) {
43924             // var touch = e.browserEvent.changedTouches[0];
43925             // this.strokeBegin(touch);
43926             
43927              this.strokeBegin(e); // assume e catching the correct xy...
43928         }
43929     },
43930     
43931     _handleTouchMove: function (e) {
43932         e.preventDefault();
43933         // var touch = event.targetTouches[0];
43934         // _this._strokeMoveUpdate(touch);
43935         this.strokeMoveUpdate(e);
43936     },
43937     
43938     _handleTouchEnd: function (e) {
43939         var wasCanvasTouched = e.target === this.canvasEl().dom;
43940         if (wasCanvasTouched) {
43941             e.preventDefault();
43942             // var touch = event.changedTouches[0];
43943             // _this._strokeEnd(touch);
43944             this.strokeEnd(e);
43945         }
43946     },
43947     
43948     reset: function () {
43949         this._lastPoints = [];
43950         this._lastVelocity = 0;
43951         this._lastWidth = (this.min_width + this.max_width) / 2;
43952         this.canvasElCtx().fillStyle = this.dot_color;
43953     },
43954     
43955     strokeMoveUpdate: function(e)
43956     {
43957         this.strokeUpdate(e);
43958         
43959         if (this.throttle) {
43960             this.throttleStroke(this.strokeUpdate, this.throttle);
43961         }
43962         else {
43963             this.strokeUpdate(e);
43964         }
43965     },
43966     
43967     strokeBegin: function(e)
43968     {
43969         var newPointGroup = {
43970             color: this.dot_color,
43971             points: []
43972         };
43973         
43974         if (typeof this.onBegin === 'function') {
43975             this.onBegin(e);
43976         }
43977         
43978         this.curve_data.push(newPointGroup);
43979         this.reset();
43980         this.strokeUpdate(e);
43981     },
43982     
43983     strokeUpdate: function(e)
43984     {
43985         var rect = this.canvasEl().dom.getBoundingClientRect();
43986         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43987         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43988         var lastPoints = lastPointGroup.points;
43989         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43990         var isLastPointTooClose = lastPoint
43991             ? point.distanceTo(lastPoint) <= this.min_distance
43992             : false;
43993         var color = lastPointGroup.color;
43994         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43995             var curve = this.addPoint(point);
43996             if (!lastPoint) {
43997                 this.drawDot({color: color, point: point});
43998             }
43999             else if (curve) {
44000                 this.drawCurve({color: color, curve: curve});
44001             }
44002             lastPoints.push({
44003                 time: point.time,
44004                 x: point.x,
44005                 y: point.y
44006             });
44007         }
44008     },
44009     
44010     strokeEnd: function(e)
44011     {
44012         this.strokeUpdate(e);
44013         if (typeof this.onEnd === 'function') {
44014             this.onEnd(e);
44015         }
44016     },
44017     
44018     addPoint:  function (point) {
44019         var _lastPoints = this._lastPoints;
44020         _lastPoints.push(point);
44021         if (_lastPoints.length > 2) {
44022             if (_lastPoints.length === 3) {
44023                 _lastPoints.unshift(_lastPoints[0]);
44024             }
44025             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44026             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44027             _lastPoints.shift();
44028             return curve;
44029         }
44030         return null;
44031     },
44032     
44033     calculateCurveWidths: function (startPoint, endPoint) {
44034         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44035             (1 - this.velocity_filter_weight) * this._lastVelocity;
44036
44037         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44038         var widths = {
44039             end: newWidth,
44040             start: this._lastWidth
44041         };
44042         
44043         this._lastVelocity = velocity;
44044         this._lastWidth = newWidth;
44045         return widths;
44046     },
44047     
44048     drawDot: function (_a) {
44049         var color = _a.color, point = _a.point;
44050         var ctx = this.canvasElCtx();
44051         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44052         ctx.beginPath();
44053         this.drawCurveSegment(point.x, point.y, width);
44054         ctx.closePath();
44055         ctx.fillStyle = color;
44056         ctx.fill();
44057     },
44058     
44059     drawCurve: function (_a) {
44060         var color = _a.color, curve = _a.curve;
44061         var ctx = this.canvasElCtx();
44062         var widthDelta = curve.endWidth - curve.startWidth;
44063         var drawSteps = Math.floor(curve.length()) * 2;
44064         ctx.beginPath();
44065         ctx.fillStyle = color;
44066         for (var i = 0; i < drawSteps; i += 1) {
44067         var t = i / drawSteps;
44068         var tt = t * t;
44069         var ttt = tt * t;
44070         var u = 1 - t;
44071         var uu = u * u;
44072         var uuu = uu * u;
44073         var x = uuu * curve.startPoint.x;
44074         x += 3 * uu * t * curve.control1.x;
44075         x += 3 * u * tt * curve.control2.x;
44076         x += ttt * curve.endPoint.x;
44077         var y = uuu * curve.startPoint.y;
44078         y += 3 * uu * t * curve.control1.y;
44079         y += 3 * u * tt * curve.control2.y;
44080         y += ttt * curve.endPoint.y;
44081         var width = curve.startWidth + ttt * widthDelta;
44082         this.drawCurveSegment(x, y, width);
44083         }
44084         ctx.closePath();
44085         ctx.fill();
44086     },
44087     
44088     drawCurveSegment: function (x, y, width) {
44089         var ctx = this.canvasElCtx();
44090         ctx.moveTo(x, y);
44091         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44092         this.is_empty = false;
44093     },
44094     
44095     clear: function()
44096     {
44097         var ctx = this.canvasElCtx();
44098         var canvas = this.canvasEl().dom;
44099         ctx.fillStyle = this.bg_color;
44100         ctx.clearRect(0, 0, canvas.width, canvas.height);
44101         ctx.fillRect(0, 0, canvas.width, canvas.height);
44102         this.curve_data = [];
44103         this.reset();
44104         this.is_empty = true;
44105     },
44106     
44107     fileEl: function()
44108     {
44109         return  this.el.select('input',true).first();
44110     },
44111     
44112     canvasEl: function()
44113     {
44114         return this.el.select('canvas',true).first();
44115     },
44116     
44117     canvasElCtx: function()
44118     {
44119         return this.el.select('canvas',true).first().dom.getContext('2d');
44120     },
44121     
44122     getImage: function(type)
44123     {
44124         if(this.is_empty) {
44125             return false;
44126         }
44127         
44128         // encryption ?
44129         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44130     },
44131     
44132     drawFromImage: function(img_src)
44133     {
44134         var img = new Image();
44135         
44136         img.onload = function(){
44137             this.canvasElCtx().drawImage(img, 0, 0);
44138         }.bind(this);
44139         
44140         img.src = img_src;
44141         
44142         this.is_empty = false;
44143     },
44144     
44145     selectImage: function()
44146     {
44147         this.fileEl().dom.click();
44148     },
44149     
44150     uploadImage: function(e)
44151     {
44152         var reader = new FileReader();
44153         
44154         reader.onload = function(e){
44155             var img = new Image();
44156             img.onload = function(){
44157                 this.reset();
44158                 this.canvasElCtx().drawImage(img, 0, 0);
44159             }.bind(this);
44160             img.src = e.target.result;
44161         }.bind(this);
44162         
44163         reader.readAsDataURL(e.target.files[0]);
44164     },
44165     
44166     // Bezier Point Constructor
44167     Point: (function () {
44168         function Point(x, y, time) {
44169             this.x = x;
44170             this.y = y;
44171             this.time = time || Date.now();
44172         }
44173         Point.prototype.distanceTo = function (start) {
44174             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44175         };
44176         Point.prototype.equals = function (other) {
44177             return this.x === other.x && this.y === other.y && this.time === other.time;
44178         };
44179         Point.prototype.velocityFrom = function (start) {
44180             return this.time !== start.time
44181             ? this.distanceTo(start) / (this.time - start.time)
44182             : 0;
44183         };
44184         return Point;
44185     }()),
44186     
44187     
44188     // Bezier Constructor
44189     Bezier: (function () {
44190         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44191             this.startPoint = startPoint;
44192             this.control2 = control2;
44193             this.control1 = control1;
44194             this.endPoint = endPoint;
44195             this.startWidth = startWidth;
44196             this.endWidth = endWidth;
44197         }
44198         Bezier.fromPoints = function (points, widths, scope) {
44199             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44200             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44201             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44202         };
44203         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44204             var dx1 = s1.x - s2.x;
44205             var dy1 = s1.y - s2.y;
44206             var dx2 = s2.x - s3.x;
44207             var dy2 = s2.y - s3.y;
44208             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44209             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44210             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44211             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44212             var dxm = m1.x - m2.x;
44213             var dym = m1.y - m2.y;
44214             var k = l2 / (l1 + l2);
44215             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44216             var tx = s2.x - cm.x;
44217             var ty = s2.y - cm.y;
44218             return {
44219                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44220                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44221             };
44222         };
44223         Bezier.prototype.length = function () {
44224             var steps = 10;
44225             var length = 0;
44226             var px;
44227             var py;
44228             for (var i = 0; i <= steps; i += 1) {
44229                 var t = i / steps;
44230                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44231                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44232                 if (i > 0) {
44233                     var xdiff = cx - px;
44234                     var ydiff = cy - py;
44235                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44236                 }
44237                 px = cx;
44238                 py = cy;
44239             }
44240             return length;
44241         };
44242         Bezier.prototype.point = function (t, start, c1, c2, end) {
44243             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44244             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44245             + (3.0 * c2 * (1.0 - t) * t * t)
44246             + (end * t * t * t);
44247         };
44248         return Bezier;
44249     }()),
44250     
44251     throttleStroke: function(fn, wait) {
44252       if (wait === void 0) { wait = 250; }
44253       var previous = 0;
44254       var timeout = null;
44255       var result;
44256       var storedContext;
44257       var storedArgs;
44258       var later = function () {
44259           previous = Date.now();
44260           timeout = null;
44261           result = fn.apply(storedContext, storedArgs);
44262           if (!timeout) {
44263               storedContext = null;
44264               storedArgs = [];
44265           }
44266       };
44267       return function wrapper() {
44268           var args = [];
44269           for (var _i = 0; _i < arguments.length; _i++) {
44270               args[_i] = arguments[_i];
44271           }
44272           var now = Date.now();
44273           var remaining = wait - (now - previous);
44274           storedContext = this;
44275           storedArgs = args;
44276           if (remaining <= 0 || remaining > wait) {
44277               if (timeout) {
44278                   clearTimeout(timeout);
44279                   timeout = null;
44280               }
44281               previous = now;
44282               result = fn.apply(storedContext, storedArgs);
44283               if (!timeout) {
44284                   storedContext = null;
44285                   storedArgs = [];
44286               }
44287           }
44288           else if (!timeout) {
44289               timeout = window.setTimeout(later, remaining);
44290           }
44291           return result;
44292       };
44293   }
44294   
44295 });
44296
44297  
44298
44299