Roo/bootstrap/Popover.js
[roojs1] / roojs-bootstrap-debug.js
1 /**
2  * set the version of bootstrap based on the stylesheet...
3  *
4  */
5
6 Roo.bootstrap.version = ( function() {
7     var ret=3;
8     Roo.each(document.styleSheets, function(s) {
9         if ( s.href  && s.href.match(/css-bootstrap4/)) {
10             ret=4;
11         }
12     });
13     if (ret > 3) {
14          Roo.Element.prototype.visibilityMode = Roo.Element.DISPLAY;
15     }
16     return ret;
17 })(); /*
18  * Based on:
19  * Ext JS Library 1.1.1
20  * Copyright(c) 2006-2007, Ext JS, LLC.
21  *
22  * Originally Released Under LGPL - original licence link has changed is not relivant.
23  *
24  * Fork - LGPL
25  * <script type="text/javascript">
26  */
27
28
29 /**
30  * @class Roo.Shadow
31  * Simple class that can provide a shadow effect for any element.  Note that the element MUST be absolutely positioned,
32  * and the shadow does not provide any shimming.  This should be used only in simple cases -- for more advanced
33  * functionality that can also provide the same shadow effect, see the {@link Roo.Layer} class.
34  * @constructor
35  * Create a new Shadow
36  * @param {Object} config The config object
37  */
38 Roo.Shadow = function(config){
39     Roo.apply(this, config);
40     if(typeof this.mode != "string"){
41         this.mode = this.defaultMode;
42     }
43     var o = this.offset, a = {h: 0};
44     var rad = Math.floor(this.offset/2);
45     switch(this.mode.toLowerCase()){ // all this hideous nonsense calculates the various offsets for shadows
46         case "drop":
47             a.w = 0;
48             a.l = a.t = o;
49             a.t -= 1;
50             if(Roo.isIE){
51                 a.l -= this.offset + rad;
52                 a.t -= this.offset + rad;
53                 a.w -= rad;
54                 a.h -= rad;
55                 a.t += 1;
56             }
57         break;
58         case "sides":
59             a.w = (o*2);
60             a.l = -o;
61             a.t = o-1;
62             if(Roo.isIE){
63                 a.l -= (this.offset - rad);
64                 a.t -= this.offset + rad;
65                 a.l += 1;
66                 a.w -= (this.offset - rad)*2;
67                 a.w -= rad + 1;
68                 a.h -= 1;
69             }
70         break;
71         case "frame":
72             a.w = a.h = (o*2);
73             a.l = a.t = -o;
74             a.t += 1;
75             a.h -= 2;
76             if(Roo.isIE){
77                 a.l -= (this.offset - rad);
78                 a.t -= (this.offset - rad);
79                 a.l += 1;
80                 a.w -= (this.offset + rad + 1);
81                 a.h -= (this.offset + rad);
82                 a.h += 1;
83             }
84         break;
85     };
86
87     this.adjusts = a;
88 };
89
90 Roo.Shadow.prototype = {
91     /**
92      * @cfg {String} mode
93      * The shadow display mode.  Supports the following options:<br />
94      * sides: Shadow displays on both sides and bottom only<br />
95      * frame: Shadow displays equally on all four sides<br />
96      * drop: Traditional bottom-right drop shadow (default)
97      */
98     /**
99      * @cfg {String} offset
100      * The number of pixels to offset the shadow from the element (defaults to 4)
101      */
102     offset: 4,
103
104     // private
105     defaultMode: "drop",
106
107     /**
108      * Displays the shadow under the target element
109      * @param {String/HTMLElement/Element} targetEl The id or element under which the shadow should display
110      */
111     show : function(target){
112         target = Roo.get(target);
113         if(!this.el){
114             this.el = Roo.Shadow.Pool.pull();
115             if(this.el.dom.nextSibling != target.dom){
116                 this.el.insertBefore(target);
117             }
118         }
119         this.el.setStyle("z-index", this.zIndex || parseInt(target.getStyle("z-index"), 10)-1);
120         if(Roo.isIE){
121             this.el.dom.style.filter="progid:DXImageTransform.Microsoft.alpha(opacity=50) progid:DXImageTransform.Microsoft.Blur(pixelradius="+(this.offset)+")";
122         }
123         this.realign(
124             target.getLeft(true),
125             target.getTop(true),
126             target.getWidth(),
127             target.getHeight()
128         );
129         this.el.dom.style.display = "block";
130     },
131
132     /**
133      * Returns true if the shadow is visible, else false
134      */
135     isVisible : function(){
136         return this.el ? true : false;  
137     },
138
139     /**
140      * Direct alignment when values are already available. Show must be called at least once before
141      * calling this method to ensure it is initialized.
142      * @param {Number} left The target element left position
143      * @param {Number} top The target element top position
144      * @param {Number} width The target element width
145      * @param {Number} height The target element height
146      */
147     realign : function(l, t, w, h){
148         if(!this.el){
149             return;
150         }
151         var a = this.adjusts, d = this.el.dom, s = d.style;
152         var iea = 0;
153         s.left = (l+a.l)+"px";
154         s.top = (t+a.t)+"px";
155         var sw = (w+a.w), sh = (h+a.h), sws = sw +"px", shs = sh + "px";
156  
157         if(s.width != sws || s.height != shs){
158             s.width = sws;
159             s.height = shs;
160             if(!Roo.isIE){
161                 var cn = d.childNodes;
162                 var sww = Math.max(0, (sw-12))+"px";
163                 cn[0].childNodes[1].style.width = sww;
164                 cn[1].childNodes[1].style.width = sww;
165                 cn[2].childNodes[1].style.width = sww;
166                 cn[1].style.height = Math.max(0, (sh-12))+"px";
167             }
168         }
169     },
170
171     /**
172      * Hides this shadow
173      */
174     hide : function(){
175         if(this.el){
176             this.el.dom.style.display = "none";
177             Roo.Shadow.Pool.push(this.el);
178             delete this.el;
179         }
180     },
181
182     /**
183      * Adjust the z-index of this shadow
184      * @param {Number} zindex The new z-index
185      */
186     setZIndex : function(z){
187         this.zIndex = z;
188         if(this.el){
189             this.el.setStyle("z-index", z);
190         }
191     }
192 };
193
194 // Private utility class that manages the internal Shadow cache
195 Roo.Shadow.Pool = function(){
196     var p = [];
197     var markup = Roo.isIE ?
198                  '<div class="x-ie-shadow"></div>' :
199                  '<div class="x-shadow"><div class="xst"><div class="xstl"></div><div class="xstc"></div><div class="xstr"></div></div><div class="xsc"><div class="xsml"></div><div class="xsmc"></div><div class="xsmr"></div></div><div class="xsb"><div class="xsbl"></div><div class="xsbc"></div><div class="xsbr"></div></div></div>';
200     return {
201         pull : function(){
202             var sh = p.shift();
203             if(!sh){
204                 sh = Roo.get(Roo.DomHelper.insertHtml("beforeBegin", document.body.firstChild, markup));
205                 sh.autoBoxAdjust = false;
206             }
207             return sh;
208         },
209
210         push : function(sh){
211             p.push(sh);
212         }
213     };
214 }();/*
215  * - LGPL
216  *
217  * base class for bootstrap elements.
218  * 
219  */
220
221 Roo.bootstrap = Roo.bootstrap || {};
222 /**
223  * @class Roo.bootstrap.Component
224  * @extends Roo.Component
225  * Bootstrap Component base class
226  * @cfg {String} cls css class
227  * @cfg {String} style any extra css
228  * @cfg {Object} xattr extra attributes to add to 'element' (used by builder to store stuff.)
229  * @cfg {Boolean} can_build_overlaid  True if element can be rebuild from a HTML page
230  * @cfg {string} dataId cutomer id
231  * @cfg {string} name Specifies name attribute
232  * @cfg {string} tooltip  Text for the tooltip
233  * @cfg {string} container_method method to fetch parents container element (used by NavHeaderbar -  getHeaderChildContainer)
234  * @cfg {string|object} visibilityEl (el|parent) What element to use for visibility (@see getVisibilityEl())
235  
236  * @constructor
237  * Do not use directly - it does not do anything..
238  * @param {Object} config The config object
239  */
240
241
242
243 Roo.bootstrap.Component = function(config){
244     Roo.bootstrap.Component.superclass.constructor.call(this, config);
245        
246     this.addEvents({
247         /**
248          * @event childrenrendered
249          * Fires when the children have been rendered..
250          * @param {Roo.bootstrap.Component} this
251          */
252         "childrenrendered" : true
253         
254         
255         
256     });
257     
258     
259 };
260
261 Roo.extend(Roo.bootstrap.Component, Roo.BoxComponent,  {
262     
263     
264     allowDomMove : false, // to stop relocations in parent onRender...
265     
266     cls : false,
267     
268     style : false,
269     
270     autoCreate : false,
271     
272     tooltip : null,
273     /**
274      * Initialize Events for the element
275      */
276     initEvents : function() { },
277     
278     xattr : false,
279     
280     parentId : false,
281     
282     can_build_overlaid : true,
283     
284     container_method : false,
285     
286     dataId : false,
287     
288     name : false,
289     
290     parent: function() {
291         // returns the parent component..
292         return Roo.ComponentMgr.get(this.parentId)
293         
294         
295     },
296     
297     // private
298     onRender : function(ct, position)
299     {
300        // Roo.log("Call onRender: " + this.xtype);
301         
302         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
303         
304         if(this.el){
305             if (this.el.attr('xtype')) {
306                 this.el.attr('xtypex', this.el.attr('xtype'));
307                 this.el.dom.removeAttribute('xtype');
308                 
309                 this.initEvents();
310             }
311             
312             return;
313         }
314         
315          
316         
317         var cfg = Roo.apply({},  this.getAutoCreate());
318         
319         cfg.id = this.id || Roo.id();
320         
321         // fill in the extra attributes 
322         if (this.xattr && typeof(this.xattr) =='object') {
323             for (var i in this.xattr) {
324                 cfg[i] = this.xattr[i];
325             }
326         }
327         
328         if(this.dataId){
329             cfg.dataId = this.dataId;
330         }
331         
332         if (this.cls) {
333             cfg.cls = (typeof(cfg.cls) == 'undefined') ? this.cls : cfg.cls + ' ' + this.cls;
334         }
335         
336         if (this.style) { // fixme needs to support more complex style data.
337             cfg.style = this.style;
338         }
339         
340         if(this.name){
341             cfg.name = this.name;
342         }
343         
344         this.el = ct.createChild(cfg, position);
345         
346         if (this.tooltip) {
347             this.tooltipEl().attr('tooltip', this.tooltip);
348         }
349         
350         if(this.tabIndex !== undefined){
351             this.el.dom.setAttribute('tabIndex', this.tabIndex);
352         }
353         
354         this.initEvents();
355         
356     },
357     /**
358      * Fetch the element to add children to
359      * @return {Roo.Element} defaults to this.el
360      */
361     getChildContainer : function()
362     {
363         return this.el;
364     },
365     /**
366      * Fetch the element to display the tooltip on.
367      * @return {Roo.Element} defaults to this.el
368      */
369     tooltipEl : function()
370     {
371         return this.el;
372     },
373         
374     addxtype  : function(tree,cntr)
375     {
376         var cn = this;
377         
378         cn = Roo.factory(tree);
379         //Roo.log(['addxtype', cn]);
380            
381         cn.parentType = this.xtype; //??
382         cn.parentId = this.id;
383         
384         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
385         if (typeof(cn.container_method) == 'string') {
386             cntr = cn.container_method;
387         }
388         
389         
390         var has_flexy_each =  (typeof(tree['flexy:foreach']) != 'undefined');
391         
392         var has_flexy_if =  (typeof(tree['flexy:if']) != 'undefined');
393         
394         var build_from_html =  Roo.XComponent.build_from_html;
395           
396         var is_body  = (tree.xtype == 'Body') ;
397           
398         var page_has_body = (Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body');
399           
400         var self_cntr_el = Roo.get(this[cntr](false));
401         
402         // do not try and build conditional elements 
403         if ((has_flexy_each || has_flexy_if || this.can_build_overlaid == false ) && build_from_html) {
404             return false;
405         }
406         
407         if (!has_flexy_each || !build_from_html || is_body || !page_has_body) {
408             if(!has_flexy_if || typeof(tree.name) == 'undefined' || !build_from_html || is_body || !page_has_body){
409                 return this.addxtypeChild(tree,cntr, is_body);
410             }
411             
412             var echild =self_cntr_el ? self_cntr_el.child('>*[name=' + tree.name + ']') : false;
413                 
414             if(echild){
415                 return this.addxtypeChild(Roo.apply({}, tree),cntr);
416             }
417             
418             Roo.log('skipping render');
419             return cn;
420             
421         }
422         
423         var ret = false;
424         if (!build_from_html) {
425             return false;
426         }
427         
428         // this i think handles overlaying multiple children of the same type
429         // with the sam eelement.. - which might be buggy..
430         while (true) {
431             var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
432             
433             if (!echild) {
434                 break;
435             }
436             
437             if (echild && echild.attr('xtype').split('.').pop() != cn.xtype) {
438                 break;
439             }
440             
441             ret = this.addxtypeChild(Roo.apply({}, tree),cntr);
442         }
443        
444         return ret;
445     },
446     
447     
448     addxtypeChild : function (tree, cntr, is_body)
449     {
450         Roo.debug && Roo.log('addxtypeChild:' + cntr);
451         var cn = this;
452         cntr = (typeof(cntr) == 'undefined' ) ? 'getChildContainer' : cntr;
453         
454         
455         var has_flexy = (typeof(tree['flexy:if']) != 'undefined') ||
456                     (typeof(tree['flexy:foreach']) != 'undefined');
457           
458     
459         
460         skip_children = false;
461         // render the element if it's not BODY.
462         if (!is_body) {
463             
464             // if parent was disabled, then do not try and create the children..
465             if(!this[cntr](true)){
466                 tree.items = [];
467                 return tree;
468             }
469            
470             cn = Roo.factory(tree);
471            
472             cn.parentType = this.xtype; //??
473             cn.parentId = this.id;
474             
475             var build_from_html =  Roo.XComponent.build_from_html;
476             
477             
478             // does the container contain child eleemnts with 'xtype' attributes.
479             // that match this xtype..
480             // note - when we render we create these as well..
481             // so we should check to see if body has xtype set.
482             if (build_from_html && Roo.get(document.body).attr('xtype') == 'Roo.bootstrap.Body') {
483                
484                 var self_cntr_el = Roo.get(this[cntr](false));
485                 var echild =self_cntr_el ? self_cntr_el.child('>*[xtype]') : false;
486                 if (echild) { 
487                     //Roo.log(Roo.XComponent.build_from_html);
488                     //Roo.log("got echild:");
489                     //Roo.log(echild);
490                 }
491                 // there is a scenario where some of the child elements are flexy:if (and all of the same type)
492                 // and are not displayed -this causes this to use up the wrong element when matching.
493                 // at present the only work around for this is to nest flexy:if elements in another element that is always rendered.
494                 
495                 
496                 if (echild && echild.attr('xtype').split('.').pop() == cn.xtype) {
497                   //  Roo.log("found child for " + this.xtype +": " + echild.attr('xtype') );
498                   
499                   
500                   
501                     cn.el = echild;
502                   //  Roo.log("GOT");
503                     //echild.dom.removeAttribute('xtype');
504                 } else {
505                     Roo.debug && Roo.log("MISSING " + cn.xtype + " on child of " + (this.el ? this.el.attr('xbuilderid') : 'no parent'));
506                     Roo.debug && Roo.log(self_cntr_el);
507                     Roo.debug && Roo.log(echild);
508                     Roo.debug && Roo.log(cn);
509                 }
510             }
511            
512             
513            
514             // if object has flexy:if - then it may or may not be rendered.
515             if (build_from_html && has_flexy && !cn.el &&  cn.can_build_overlaid) {
516                 // skip a flexy if element.
517                 Roo.debug && Roo.log('skipping render');
518                 Roo.debug && Roo.log(tree);
519                 if (!cn.el) {
520                     Roo.debug && Roo.log('skipping all children');
521                     skip_children = true;
522                 }
523                 
524              } else {
525                  
526                 // actually if flexy:foreach is found, we really want to create 
527                 // multiple copies here...
528                 //Roo.log('render');
529                 //Roo.log(this[cntr]());
530                 // some elements do not have render methods.. like the layouts...
531                 /*
532                 if(this[cntr](true) === false){
533                     cn.items = [];
534                     return cn;
535                 }
536                 */
537                 cn.render && cn.render(this[cntr](true));
538                 
539              }
540             // then add the element..
541         }
542          
543         // handle the kids..
544         
545         var nitems = [];
546         /*
547         if (typeof (tree.menu) != 'undefined') {
548             tree.menu.parentType = cn.xtype;
549             tree.menu.triggerEl = cn.el;
550             nitems.push(cn.addxtype(Roo.apply({}, tree.menu)));
551             
552         }
553         */
554         if (!tree.items || !tree.items.length) {
555             cn.items = nitems;
556             //Roo.log(["no children", this]);
557             
558             return cn;
559         }
560          
561         var items = tree.items;
562         delete tree.items;
563         
564         //Roo.log(items.length);
565             // add the items..
566         if (!skip_children) {    
567             for(var i =0;i < items.length;i++) {
568               //  Roo.log(['add child', items[i]]);
569                 nitems.push(cn.addxtype(Roo.apply({}, items[i])));
570             }
571         }
572         
573         cn.items = nitems;
574         
575         //Roo.log("fire childrenrendered");
576         
577         cn.fireEvent('childrenrendered', this);
578         
579         return cn;
580     },
581     
582     /**
583      * Set the element that will be used to show or hide
584      */
585     setVisibilityEl : function(el)
586     {
587         this.visibilityEl = el;
588     },
589     
590      /**
591      * Get the element that will be used to show or hide
592      */
593     getVisibilityEl : function()
594     {
595         if (typeof(this.visibilityEl) == 'object') {
596             return this.visibilityEl;
597         }
598         
599         if (typeof(this.visibilityEl) == 'string') {
600             return this.visibilityEl == 'parent' ? this.parent().getEl() : this.getEl();
601         }
602         
603         return this.getEl();
604     },
605     
606     /**
607      * Show a component - removes 'hidden' class
608      */
609     show : function()
610     {
611         if(!this.getVisibilityEl()){
612             return;
613         }
614          
615         this.getVisibilityEl().removeClass(['hidden','d-none']);
616         
617         this.fireEvent('show', this);
618         
619         
620     },
621     /**
622      * Hide a component - adds 'hidden' class
623      */
624     hide: function()
625     {
626         if(!this.getVisibilityEl()){
627             return;
628         }
629         
630         this.getVisibilityEl().addClass(['hidden','d-none']);
631         
632         this.fireEvent('hide', this);
633         
634     }
635 });
636
637  /*
638  * - LGPL
639  *
640  * element
641  * 
642  */
643
644 /**
645  * @class Roo.bootstrap.Element
646  * @extends Roo.bootstrap.Component
647  * Bootstrap Element class
648  * @cfg {String} html contents of the element
649  * @cfg {String} tag tag of the element
650  * @cfg {String} cls class of the element
651  * @cfg {Boolean} preventDefault (true|false) default false
652  * @cfg {Boolean} clickable (true|false) default false
653  * 
654  * @constructor
655  * Create a new Element
656  * @param {Object} config The config object
657  */
658
659 Roo.bootstrap.Element = function(config){
660     Roo.bootstrap.Element.superclass.constructor.call(this, config);
661     
662     this.addEvents({
663         // raw events
664         /**
665          * @event click
666          * When a element is chick
667          * @param {Roo.bootstrap.Element} this
668          * @param {Roo.EventObject} e
669          */
670         "click" : true
671     });
672 };
673
674 Roo.extend(Roo.bootstrap.Element, Roo.bootstrap.Component,  {
675     
676     tag: 'div',
677     cls: '',
678     html: '',
679     preventDefault: false, 
680     clickable: false,
681     
682     getAutoCreate : function(){
683         
684         var cfg = {
685             tag: this.tag,
686             // cls: this.cls, double assign in parent class Component.js :: onRender
687             html: this.html
688         };
689         
690         return cfg;
691     },
692     
693     initEvents: function() 
694     {
695         Roo.bootstrap.Element.superclass.initEvents.call(this);
696         
697         if(this.clickable){
698             this.el.on('click', this.onClick, this);
699         }
700         
701     },
702     
703     onClick : function(e)
704     {
705         if(this.preventDefault){
706             e.preventDefault();
707         }
708         
709         this.fireEvent('click', this, e);
710     },
711     
712     getValue : function()
713     {
714         return this.el.dom.innerHTML;
715     },
716     
717     setValue : function(value)
718     {
719         this.el.dom.innerHTML = value;
720     }
721    
722 });
723
724  
725
726  /*
727  * - LGPL
728  *
729  * dropable area
730  * 
731  */
732
733 /**
734  * @class Roo.bootstrap.DropTarget
735  * @extends Roo.bootstrap.Element
736  * Bootstrap DropTarget class
737  
738  * @cfg {string} name dropable name
739  * 
740  * @constructor
741  * Create a new Dropable Area
742  * @param {Object} config The config object
743  */
744
745 Roo.bootstrap.DropTarget = function(config){
746     Roo.bootstrap.DropTarget.superclass.constructor.call(this, config);
747     
748     this.addEvents({
749         // raw events
750         /**
751          * @event click
752          * When a element is chick
753          * @param {Roo.bootstrap.Element} this
754          * @param {Roo.EventObject} e
755          */
756         "drop" : true
757     });
758 };
759
760 Roo.extend(Roo.bootstrap.DropTarget, Roo.bootstrap.Element,  {
761     
762     
763     getAutoCreate : function(){
764         
765          
766     },
767     
768     initEvents: function() 
769     {
770         Roo.bootstrap.DropTarget.superclass.initEvents.call(this);
771         this.dropZone = new Roo.dd.DropTarget(this.getEl(), {
772             ddGroup: this.name,
773             listeners : {
774                 drop : this.dragDrop.createDelegate(this),
775                 enter : this.dragEnter.createDelegate(this),
776                 out : this.dragOut.createDelegate(this),
777                 over : this.dragOver.createDelegate(this)
778             }
779             
780         });
781         this.dropZone.DDM.useCache = false // so data gets refreshed when we resize stuff
782     },
783     
784     dragDrop : function(source,e,data)
785     {
786         // user has to decide how to impliment this.
787         Roo.log('drop');
788         Roo.log(this);
789         //this.fireEvent('drop', this, source, e ,data);
790         return false;
791     },
792     
793     dragEnter : function(n, dd, e, data)
794     {
795         // probably want to resize the element to match the dropped element..
796         Roo.log("enter");
797         this.originalSize = this.el.getSize();
798         this.el.setSize( n.el.getSize());
799         this.dropZone.DDM.refreshCache(this.name);
800         Roo.log([n, dd, e, data]);
801     },
802     
803     dragOut : function(value)
804     {
805         // resize back to normal
806         Roo.log("out");
807         this.el.setSize(this.originalSize);
808         this.dropZone.resetConstraints();
809     },
810     
811     dragOver : function()
812     {
813         // ??? do nothing?
814     }
815    
816 });
817
818  
819
820  /*
821  * - LGPL
822  *
823  * Body
824  *
825  */
826
827 /**
828  * @class Roo.bootstrap.Body
829  * @extends Roo.bootstrap.Component
830  * Bootstrap Body class
831  *
832  * @constructor
833  * Create a new body
834  * @param {Object} config The config object
835  */
836
837 Roo.bootstrap.Body = function(config){
838
839     config = config || {};
840
841     Roo.bootstrap.Body.superclass.constructor.call(this, config);
842     this.el = Roo.get(config.el ? config.el : document.body );
843     if (this.cls && this.cls.length) {
844         Roo.get(document.body).addClass(this.cls);
845     }
846 };
847
848 Roo.extend(Roo.bootstrap.Body, Roo.bootstrap.Component,  {
849
850     is_body : true,// just to make sure it's constructed?
851
852         autoCreate : {
853         cls: 'container'
854     },
855     onRender : function(ct, position)
856     {
857        /* Roo.log("Roo.bootstrap.Body - onRender");
858         if (this.cls && this.cls.length) {
859             Roo.get(document.body).addClass(this.cls);
860         }
861         // style??? xttr???
862         */
863     }
864
865
866
867
868 });
869 /*
870  * - LGPL
871  *
872  * button group
873  * 
874  */
875
876
877 /**
878  * @class Roo.bootstrap.ButtonGroup
879  * @extends Roo.bootstrap.Component
880  * Bootstrap ButtonGroup class
881  * @cfg {String} size lg | sm | xs (default empty normal)
882  * @cfg {String} align vertical | justified  (default none)
883  * @cfg {String} direction up | down (default down)
884  * @cfg {Boolean} toolbar false | true
885  * @cfg {Boolean} btn true | false
886  * 
887  * 
888  * @constructor
889  * Create a new Input
890  * @param {Object} config The config object
891  */
892
893 Roo.bootstrap.ButtonGroup = function(config){
894     Roo.bootstrap.ButtonGroup.superclass.constructor.call(this, config);
895 };
896
897 Roo.extend(Roo.bootstrap.ButtonGroup, Roo.bootstrap.Component,  {
898     
899     size: '',
900     align: '',
901     direction: '',
902     toolbar: false,
903     btn: true,
904
905     getAutoCreate : function(){
906         var cfg = {
907             cls: 'btn-group',
908             html : null
909         };
910         
911         cfg.html = this.html || cfg.html;
912         
913         if (this.toolbar) {
914             cfg = {
915                 cls: 'btn-toolbar',
916                 html: null
917             };
918             
919             return cfg;
920         }
921         
922         if (['vertical','justified'].indexOf(this.align)!==-1) {
923             cfg.cls = 'btn-group-' + this.align;
924             
925             if (this.align == 'justified') {
926                 console.log(this.items);
927             }
928         }
929         
930         if (['lg','sm','xs'].indexOf(this.size)!==-1) {
931             cfg.cls += ' btn-group-' + this.size;
932         }
933         
934         if (this.direction == 'up') {
935             cfg.cls += ' dropup' ;
936         }
937         
938         return cfg;
939     },
940     /**
941      * Add a button to the group (similar to NavItem API.)
942      */
943     addItem : function(cfg)
944     {
945         var cn = new Roo.bootstrap.Button(cfg);
946         //this.register(cn);
947         cn.parentId = this.id;
948         cn.onRender(this.el, null);
949         return cn;
950     }
951    
952 });
953
954  /*
955  * - LGPL
956  *
957  * button
958  * 
959  */
960
961 /**
962  * @class Roo.bootstrap.Button
963  * @extends Roo.bootstrap.Component
964  * Bootstrap Button class
965  * @cfg {String} html The button content
966  * @cfg {String} weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default
967  * @cfg {String} badge_weight (default|primary|secondary|success|info|warning|danger|link|light|dark) default (same as button)
968  * @cfg {Boolean} outline default false (except for weight=default which emulates old behaveiour with an outline)
969  * @cfg {String} size (lg|sm|xs)
970  * @cfg {String} tag (a|input|submit)
971  * @cfg {String} href empty or href
972  * @cfg {Boolean} disabled default false;
973  * @cfg {Boolean} isClose default false;
974  * @cfg {String} glyphicon depricated - use fa
975  * @cfg {String} fa fontawesome icon - eg. 'comment' - without the fa/fas etc..
976  * @cfg {String} badge text for badge
977  * @cfg {String} theme (default|glow)  
978  * @cfg {Boolean} inverse dark themed version
979  * @cfg {Boolean} toggle is it a slidy toggle button
980  * @cfg {Boolean} pressed   default null - if the button ahs active state
981  * @cfg {String} ontext text for on slidy toggle state
982  * @cfg {String} offtext text for off slidy toggle state
983  * @cfg {Boolean} preventDefault  default true (stop click event triggering the URL if it's a link.)
984  * @cfg {Boolean} removeClass remove the standard class..
985  * @cfg {String} target (_self|_blank|_parent|_top|other) target for a href. 
986  * @cfg {Boolean} grpup if parent is a btn group - then it turns it into a toogleGroup.
987  * 
988  * @constructor
989  * Create a new button
990  * @param {Object} config The config object
991  */
992
993
994 Roo.bootstrap.Button = function(config){
995     Roo.bootstrap.Button.superclass.constructor.call(this, config);
996     
997     this.addEvents({
998         // raw events
999         /**
1000          * @event click
1001          * When a button is pressed
1002          * @param {Roo.bootstrap.Button} btn
1003          * @param {Roo.EventObject} e
1004          */
1005         "click" : true,
1006         /**
1007          * @event dblclick
1008          * When a button is double clicked
1009          * @param {Roo.bootstrap.Button} btn
1010          * @param {Roo.EventObject} e
1011          */
1012         "dblclick" : true,
1013          /**
1014          * @event toggle
1015          * After the button has been toggles
1016          * @param {Roo.bootstrap.Button} btn
1017          * @param {Roo.EventObject} e
1018          * @param {boolean} pressed (also available as button.pressed)
1019          */
1020         "toggle" : true
1021     });
1022 };
1023
1024 Roo.extend(Roo.bootstrap.Button, Roo.bootstrap.Component,  {
1025     html: false,
1026     active: false,
1027     weight: '',
1028     badge_weight: '',
1029     outline : false,
1030     size: '',
1031     tag: 'button',
1032     href: '',
1033     disabled: false,
1034     isClose: false,
1035     glyphicon: '',
1036     fa: '',
1037     badge: '',
1038     theme: 'default',
1039     inverse: false,
1040     
1041     toggle: false,
1042     ontext: 'ON',
1043     offtext: 'OFF',
1044     defaulton: true,
1045     preventDefault: true,
1046     removeClass: false,
1047     name: false,
1048     target: false,
1049     group : false,
1050      
1051     pressed : null,
1052      
1053     
1054     getAutoCreate : function(){
1055         
1056         var cfg = {
1057             tag : 'button',
1058             cls : 'roo-button',
1059             html: ''
1060         };
1061         
1062         if (['a', 'button', 'input', 'submit'].indexOf(this.tag) < 0) {
1063             throw "Invalid value for tag: " + this.tag + ". must be a, button, input or submit.";
1064             this.tag = 'button';
1065         } else {
1066             cfg.tag = this.tag;
1067         }
1068         cfg.html = '<span class="roo-button-text">' + (this.html || cfg.html) + '</span>';
1069         
1070         if (this.toggle == true) {
1071             cfg={
1072                 tag: 'div',
1073                 cls: 'slider-frame roo-button',
1074                 cn: [
1075                     {
1076                         tag: 'span',
1077                         'data-on-text':'ON',
1078                         'data-off-text':'OFF',
1079                         cls: 'slider-button',
1080                         html: this.offtext
1081                     }
1082                 ]
1083             };
1084             // why are we validating the weights?
1085             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1086                 cfg.cls +=  ' ' + this.weight;
1087             }
1088             
1089             return cfg;
1090         }
1091         
1092         if (this.isClose) {
1093             cfg.cls += ' close';
1094             
1095             cfg["aria-hidden"] = true;
1096             
1097             cfg.html = "&times;";
1098             
1099             return cfg;
1100         }
1101              
1102         
1103         if (this.theme==='default') {
1104             cfg.cls = 'btn roo-button';
1105             
1106             //if (this.parentType != 'Navbar') {
1107             this.weight = this.weight.length ?  this.weight : 'default';
1108             //}
1109             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1110                 
1111                 var outline = this.outline || this.weight == 'default' ? 'outline-' : '';
1112                 var weight = this.weight == 'default' ? 'secondary' : this.weight;
1113                 cfg.cls += ' btn-' + outline + weight;
1114                 if (this.weight == 'default') {
1115                     // BC
1116                     cfg.cls += ' btn-' + this.weight;
1117                 }
1118             }
1119         } else if (this.theme==='glow') {
1120             
1121             cfg.tag = 'a';
1122             cfg.cls = 'btn-glow roo-button';
1123             
1124             if (Roo.bootstrap.Button.weights.indexOf(this.weight) > -1) {
1125                 
1126                 cfg.cls += ' ' + this.weight;
1127             }
1128         }
1129    
1130         
1131         if (this.inverse) {
1132             this.cls += ' inverse';
1133         }
1134         
1135         
1136         if (this.active || this.pressed === true) {
1137             cfg.cls += ' active';
1138         }
1139         
1140         if (this.disabled) {
1141             cfg.disabled = 'disabled';
1142         }
1143         
1144         if (this.items) {
1145             Roo.log('changing to ul' );
1146             cfg.tag = 'ul';
1147             this.glyphicon = 'caret';
1148             if (Roo.bootstrap.version == 4) {
1149                 this.fa = 'caret-down';
1150             }
1151             
1152         }
1153         
1154         cfg.cls += this.size.length ? (' btn-' + this.size) : '';
1155          
1156         //gsRoo.log(this.parentType);
1157         if (this.parentType === 'Navbar' && !this.parent().bar) {
1158             Roo.log('changing to li?');
1159             
1160             cfg.tag = 'li';
1161             
1162             cfg.cls = '';
1163             cfg.cn =  [{
1164                 tag : 'a',
1165                 cls : 'roo-button',
1166                 html : this.html,
1167                 href : this.href || '#'
1168             }];
1169             if (this.menu) {
1170                 cfg.cn[0].html = this.html  + ' <span class="caret"></span>';
1171                 cfg.cls += ' dropdown';
1172             }   
1173             
1174             delete cfg.html;
1175             
1176         }
1177         
1178        cfg.cls += this.parentType === 'Navbar' ?  ' navbar-btn' : '';
1179         
1180         if (this.glyphicon) {
1181             cfg.html = ' ' + cfg.html;
1182             
1183             cfg.cn = [
1184                 {
1185                     tag: 'span',
1186                     cls: 'glyphicon glyphicon-' + this.glyphicon
1187                 }
1188             ];
1189         }
1190         if (this.fa) {
1191             cfg.html = ' ' + cfg.html;
1192             
1193             cfg.cn = [
1194                 {
1195                     tag: 'i',
1196                     cls: 'fa fas fa-' + this.fa
1197                 }
1198             ];
1199         }
1200         
1201         if (this.badge) {
1202             cfg.html += ' ';
1203             
1204             cfg.tag = 'a';
1205             
1206 //            cfg.cls='btn roo-button';
1207             
1208             cfg.href=this.href;
1209             
1210             var value = cfg.html;
1211             
1212             if(this.glyphicon){
1213                 value = {
1214                     tag: 'span',
1215                     cls: 'glyphicon glyphicon-' + this.glyphicon,
1216                     html: this.html
1217                 };
1218             }
1219             if(this.fa){
1220                 value = {
1221                     tag: 'i',
1222                     cls: 'fa fas fa-' + this.fa,
1223                     html: this.html
1224                 };
1225             }
1226             
1227             var bw = this.badge_weight.length ? this.badge_weight :
1228                 (this.weight.length ? this.weight : 'secondary');
1229             bw = bw == 'default' ? 'secondary' : bw;
1230             
1231             cfg.cn = [
1232                 value,
1233                 {
1234                     tag: 'span',
1235                     cls: 'badge badge-' + bw,
1236                     html: this.badge
1237                 }
1238             ];
1239             
1240             cfg.html='';
1241         }
1242         
1243         if (this.menu) {
1244             cfg.cls += ' dropdown';
1245             cfg.html = typeof(cfg.html) != 'undefined' ?
1246                     cfg.html + ' <span class="caret"></span>' : '<span class="caret"></span>';
1247         }
1248         
1249         if (cfg.tag !== 'a' && this.href !== '') {
1250             throw "Tag must be a to set href.";
1251         } else if (this.href.length > 0) {
1252             cfg.href = this.href;
1253         }
1254         
1255         if(this.removeClass){
1256             cfg.cls = '';
1257         }
1258         
1259         if(this.target){
1260             cfg.target = this.target;
1261         }
1262         
1263         return cfg;
1264     },
1265     initEvents: function() {
1266        // Roo.log('init events?');
1267 //        Roo.log(this.el.dom);
1268         // add the menu...
1269         
1270         if (typeof (this.menu) != 'undefined') {
1271             this.menu.parentType = this.xtype;
1272             this.menu.triggerEl = this.el;
1273             this.addxtype(Roo.apply({}, this.menu));
1274         }
1275
1276
1277         if (this.el.hasClass('roo-button')) {
1278              this.el.on('click', this.onClick, this);
1279              this.el.on('dblclick', this.onDblClick, this);
1280         } else {
1281              this.el.select('.roo-button').on('click', this.onClick, this);
1282              this.el.select('.roo-button').on('dblclick', this.onDblClick, this);
1283              
1284         }
1285         // why?
1286         if(this.removeClass){
1287             this.el.on('click', this.onClick, this);
1288         }
1289         
1290         if (this.group === true) {
1291              if (this.pressed === false || this.pressed === true) {
1292                 // nothing
1293             } else {
1294                 this.pressed = false;
1295                 this.setActive(this.pressed);
1296             }
1297             
1298         }
1299         
1300         this.el.enableDisplayMode();
1301         
1302     },
1303     onClick : function(e)
1304     {
1305         if (this.disabled) {
1306             return;
1307         }
1308         
1309         Roo.log('button on click ');
1310         if(this.preventDefault){
1311             e.preventDefault();
1312         }
1313         
1314         if (this.group) {
1315             if (this.pressed) {
1316                 // do nothing -
1317                 return;
1318             }
1319             this.setActive(true);
1320             var pi = this.parent().items;
1321             for (var i = 0;i < pi.length;i++) {
1322                 if (this == pi[i]) {
1323                     continue;
1324                 }
1325                 if (pi[i].el.hasClass('roo-button')) {
1326                     pi[i].setActive(false);
1327                 }
1328             }
1329             this.fireEvent('click', this, e);            
1330             return;
1331         }
1332         
1333         if (this.pressed === true || this.pressed === false) {
1334             this.toggleActive(e);
1335         }
1336         
1337         
1338         this.fireEvent('click', this, e);
1339     },
1340     onDblClick: function(e)
1341     {
1342         if (this.disabled) {
1343             return;
1344         }
1345         if(this.preventDefault){
1346             e.preventDefault();
1347         }
1348         this.fireEvent('dblclick', this, e);
1349     },
1350     /**
1351      * Enables this button
1352      */
1353     enable : function()
1354     {
1355         this.disabled = false;
1356         this.el.removeClass('disabled');
1357     },
1358     
1359     /**
1360      * Disable this button
1361      */
1362     disable : function()
1363     {
1364         this.disabled = true;
1365         this.el.addClass('disabled');
1366     },
1367      /**
1368      * sets the active state on/off, 
1369      * @param {Boolean} state (optional) Force a particular state
1370      */
1371     setActive : function(v) {
1372         
1373         this.el[v ? 'addClass' : 'removeClass']('active');
1374         this.pressed = v;
1375     },
1376      /**
1377      * toggles the current active state 
1378      */
1379     toggleActive : function(e)
1380     {
1381         this.setActive(!this.pressed); // this modifies pressed...
1382         this.fireEvent('toggle', this, e, this.pressed);
1383     },
1384      /**
1385      * get the current active state
1386      * @return {boolean} true if it's active
1387      */
1388     isActive : function()
1389     {
1390         return this.el.hasClass('active');
1391     },
1392     /**
1393      * set the text of the first selected button
1394      */
1395     setText : function(str)
1396     {
1397         this.el.select('.roo-button-text',true).first().dom.innerHTML = str;
1398     },
1399     /**
1400      * get the text of the first selected button
1401      */
1402     getText : function()
1403     {
1404         return this.el.select('.roo-button-text',true).first().dom.innerHTML;
1405     },
1406     
1407     setWeight : function(str)
1408     {
1409         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-' + w; } ) );
1410         this.el.removeClass(Roo.bootstrap.Button.weights.map(function(w) { return 'btn-outline-' + w; } ) );
1411         this.weight = str;
1412         var outline = this.outline ? 'outline-' : '';
1413         if (str == 'default') {
1414             this.el.addClass('btn-default btn-outline-secondary');        
1415             return;
1416         }
1417         this.el.addClass('btn-' + outline + str);        
1418     }
1419     
1420     
1421 });
1422 // fixme - this is probably generic bootstrap - should go in some kind of enum file.. - like sizes.
1423
1424 Roo.bootstrap.Button.weights = [
1425     'default',
1426     'secondary' ,
1427     'primary',
1428     'success',
1429     'info',
1430     'warning',
1431     'danger',
1432     'link',
1433     'light',
1434     'dark'              
1435    
1436 ];/*
1437  * - LGPL
1438  *
1439  * column
1440  * 
1441  */
1442
1443 /**
1444  * @class Roo.bootstrap.Column
1445  * @extends Roo.bootstrap.Component
1446  * Bootstrap Column class
1447  * @cfg {Number} xs colspan out of 12 for mobile-sized screens or 0 for hidden
1448  * @cfg {Number} sm colspan out of 12 for tablet-sized screens or 0 for hidden
1449  * @cfg {Number} md colspan out of 12 for computer-sized screens or 0 for hidden
1450  * @cfg {Number} lg colspan out of 12 for large computer-sized screens or 0 for hidden
1451  * @cfg {Number} xsoff colspan offset out of 12 for mobile-sized screens or 0 for hidden
1452  * @cfg {Number} smoff colspan offset out of 12 for tablet-sized screens or 0 for hidden
1453  * @cfg {Number} mdoff colspan offset out of 12 for computer-sized screens or 0 for hidden
1454  * @cfg {Number} lgoff colspan offset out of 12 for large computer-sized screens or 0 for hidden
1455  *
1456  * 
1457  * @cfg {Boolean} hidden (true|false) hide the element
1458  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1459  * @cfg {String} fa (ban|check|...) font awesome icon
1460  * @cfg {Number} fasize (1|2|....) font awsome size
1461
1462  * @cfg {String} icon (info-sign|check|...) glyphicon name
1463
1464  * @cfg {String} html content of column.
1465  * 
1466  * @constructor
1467  * Create a new Column
1468  * @param {Object} config The config object
1469  */
1470
1471 Roo.bootstrap.Column = function(config){
1472     Roo.bootstrap.Column.superclass.constructor.call(this, config);
1473 };
1474
1475 Roo.extend(Roo.bootstrap.Column, Roo.bootstrap.Component,  {
1476     
1477     xs: false,
1478     sm: false,
1479     md: false,
1480     lg: false,
1481     xsoff: false,
1482     smoff: false,
1483     mdoff: false,
1484     lgoff: false,
1485     html: '',
1486     offset: 0,
1487     alert: false,
1488     fa: false,
1489     icon : false,
1490     hidden : false,
1491     fasize : 1,
1492     
1493     getAutoCreate : function(){
1494         var cfg = Roo.apply({}, Roo.bootstrap.Column.superclass.getAutoCreate.call(this));
1495         
1496         cfg = {
1497             tag: 'div',
1498             cls: 'column'
1499         };
1500         
1501         var settings=this;
1502         var sizes =   ['xs','sm','md','lg'];
1503         sizes.map(function(size ,ix){
1504             //Roo.log( size + ':' + settings[size]);
1505             
1506             if (settings[size+'off'] !== false) {
1507                 cfg.cls += ' col-' + size + '-offset-' + settings[size+'off'] ;
1508             }
1509             
1510             if (settings[size] === false) {
1511                 return;
1512             }
1513             
1514             if (!settings[size]) { // 0 = hidden
1515                 cfg.cls += ' hidden-' + size + ' hidden-' + size + '-down';
1516                 // bootsrap4
1517                 for (var i = ix; i > -1; i--) {
1518                     cfg.cls +=  ' d-' + sizes[i] + '-none'; 
1519                 }
1520                 
1521                 
1522                 return;
1523             }
1524             cfg.cls += ' col-' + size + '-' + settings[size] + (
1525                 size == 'xs' ? (' col-' + settings[size] ) : '' // bs4 col-{num} replaces col-xs
1526             );
1527             
1528         });
1529         
1530         if (this.hidden) {
1531             cfg.cls += ' hidden';
1532         }
1533         
1534         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1535             cfg.cls +=' alert alert-' + this.alert;
1536         }
1537         
1538         
1539         if (this.html.length) {
1540             cfg.html = this.html;
1541         }
1542         if (this.fa) {
1543             var fasize = '';
1544             if (this.fasize > 1) {
1545                 fasize = ' fa-' + this.fasize + 'x';
1546             }
1547             cfg.html = '<i class="fa fa-'+this.fa + fasize + '"></i>' + (cfg.html || '');
1548             
1549             
1550         }
1551         if (this.icon) {
1552             cfg.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' +  (cfg.html || '');
1553         }
1554         
1555         return cfg;
1556     }
1557    
1558 });
1559
1560  
1561
1562  /*
1563  * - LGPL
1564  *
1565  * page container.
1566  * 
1567  */
1568
1569
1570 /**
1571  * @class Roo.bootstrap.Container
1572  * @extends Roo.bootstrap.Component
1573  * Bootstrap Container class
1574  * @cfg {Boolean} jumbotron is it a jumbotron element
1575  * @cfg {String} html content of element
1576  * @cfg {String} well (lg|sm|md) a well, large, small or medium.
1577  * @cfg {String} panel (default|primary|success|info|warning|danger) render as panel  - type - primary/success.....
1578  * @cfg {String} header content of header (for panel)
1579  * @cfg {String} footer content of footer (for panel)
1580  * @cfg {String} sticky (footer|wrap|push) block to use as footer or body- needs css-bootstrap/sticky-footer.css
1581  * @cfg {String} tag (header|aside|section) type of HTML tag.
1582  * @cfg {String} alert (success|info|warning|danger) type alert (changes background / border...)
1583  * @cfg {String} fa font awesome icon
1584  * @cfg {String} icon (info-sign|check|...) glyphicon name
1585  * @cfg {Boolean} hidden (true|false) hide the element
1586  * @cfg {Boolean} expandable (true|false) default false
1587  * @cfg {Boolean} expanded (true|false) default true
1588  * @cfg {String} rheader contet on the right of header
1589  * @cfg {Boolean} clickable (true|false) default false
1590
1591  *     
1592  * @constructor
1593  * Create a new Container
1594  * @param {Object} config The config object
1595  */
1596
1597 Roo.bootstrap.Container = function(config){
1598     Roo.bootstrap.Container.superclass.constructor.call(this, config);
1599     
1600     this.addEvents({
1601         // raw events
1602          /**
1603          * @event expand
1604          * After the panel has been expand
1605          * 
1606          * @param {Roo.bootstrap.Container} this
1607          */
1608         "expand" : true,
1609         /**
1610          * @event collapse
1611          * After the panel has been collapsed
1612          * 
1613          * @param {Roo.bootstrap.Container} this
1614          */
1615         "collapse" : true,
1616         /**
1617          * @event click
1618          * When a element is chick
1619          * @param {Roo.bootstrap.Container} this
1620          * @param {Roo.EventObject} e
1621          */
1622         "click" : true
1623     });
1624 };
1625
1626 Roo.extend(Roo.bootstrap.Container, Roo.bootstrap.Component,  {
1627     
1628     jumbotron : false,
1629     well: '',
1630     panel : '',
1631     header: '',
1632     footer : '',
1633     sticky: '',
1634     tag : false,
1635     alert : false,
1636     fa: false,
1637     icon : false,
1638     expandable : false,
1639     rheader : '',
1640     expanded : true,
1641     clickable: false,
1642   
1643      
1644     getChildContainer : function() {
1645         
1646         if(!this.el){
1647             return false;
1648         }
1649         
1650         if (this.panel.length) {
1651             return this.el.select('.panel-body',true).first();
1652         }
1653         
1654         return this.el;
1655     },
1656     
1657     
1658     getAutoCreate : function(){
1659         
1660         var cfg = {
1661             tag : this.tag || 'div',
1662             html : '',
1663             cls : ''
1664         };
1665         if (this.jumbotron) {
1666             cfg.cls = 'jumbotron';
1667         }
1668         
1669         
1670         
1671         // - this is applied by the parent..
1672         //if (this.cls) {
1673         //    cfg.cls = this.cls + '';
1674         //}
1675         
1676         if (this.sticky.length) {
1677             
1678             var bd = Roo.get(document.body);
1679             if (!bd.hasClass('bootstrap-sticky')) {
1680                 bd.addClass('bootstrap-sticky');
1681                 Roo.select('html',true).setStyle('height', '100%');
1682             }
1683              
1684             cfg.cls += 'bootstrap-sticky-' + this.sticky;
1685         }
1686         
1687         
1688         if (this.well.length) {
1689             switch (this.well) {
1690                 case 'lg':
1691                 case 'sm':
1692                     cfg.cls +=' well well-' +this.well;
1693                     break;
1694                 default:
1695                     cfg.cls +=' well';
1696                     break;
1697             }
1698         }
1699         
1700         if (this.hidden) {
1701             cfg.cls += ' hidden';
1702         }
1703         
1704         
1705         if (this.alert && ["success","info","warning", "danger"].indexOf(this.alert) > -1) {
1706             cfg.cls +=' alert alert-' + this.alert;
1707         }
1708         
1709         var body = cfg;
1710         
1711         if (this.panel.length) {
1712             cfg.cls += ' panel panel-' + this.panel;
1713             cfg.cn = [];
1714             if (this.header.length) {
1715                 
1716                 var h = [];
1717                 
1718                 if(this.expandable){
1719                     
1720                     cfg.cls = cfg.cls + ' expandable';
1721                     
1722                     h.push({
1723                         tag: 'i',
1724                         cls: (this.expanded ? 'fa fa-minus' : 'fa fa-plus') 
1725                     });
1726                     
1727                 }
1728                 
1729                 h.push(
1730                     {
1731                         tag: 'span',
1732                         cls : 'panel-title',
1733                         html : (this.expandable ? '&nbsp;' : '') + this.header
1734                     },
1735                     {
1736                         tag: 'span',
1737                         cls: 'panel-header-right',
1738                         html: this.rheader
1739                     }
1740                 );
1741                 
1742                 cfg.cn.push({
1743                     cls : 'panel-heading',
1744                     style : this.expandable ? 'cursor: pointer' : '',
1745                     cn : h
1746                 });
1747                 
1748             }
1749             
1750             body = false;
1751             cfg.cn.push({
1752                 cls : 'panel-body' + (this.expanded ? '' : ' hide'),
1753                 html : this.html
1754             });
1755             
1756             
1757             if (this.footer.length) {
1758                 cfg.cn.push({
1759                     cls : 'panel-footer',
1760                     html : this.footer
1761                     
1762                 });
1763             }
1764             
1765         }
1766         
1767         if (body) {
1768             body.html = this.html || cfg.html;
1769             // prefix with the icons..
1770             if (this.fa) {
1771                 body.html = '<i class="fa fa-'+this.fa + '"></i>' + body.html ;
1772             }
1773             if (this.icon) {
1774                 body.html = '<i class="glyphicon glyphicon-'+this.icon + '"></i>' + body.html ;
1775             }
1776             
1777             
1778         }
1779         if ((!this.cls || !this.cls.length) && (!cfg.cls || !cfg.cls.length)) {
1780             cfg.cls =  'container';
1781         }
1782         
1783         return cfg;
1784     },
1785     
1786     initEvents: function() 
1787     {
1788         if(this.expandable){
1789             var headerEl = this.headerEl();
1790         
1791             if(headerEl){
1792                 headerEl.on('click', this.onToggleClick, this);
1793             }
1794         }
1795         
1796         if(this.clickable){
1797             this.el.on('click', this.onClick, this);
1798         }
1799         
1800     },
1801     
1802     onToggleClick : function()
1803     {
1804         var headerEl = this.headerEl();
1805         
1806         if(!headerEl){
1807             return;
1808         }
1809         
1810         if(this.expanded){
1811             this.collapse();
1812             return;
1813         }
1814         
1815         this.expand();
1816     },
1817     
1818     expand : function()
1819     {
1820         if(this.fireEvent('expand', this)) {
1821             
1822             this.expanded = true;
1823             
1824             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).show();
1825             
1826             this.el.select('.panel-body',true).first().removeClass('hide');
1827             
1828             var toggleEl = this.toggleEl();
1829
1830             if(!toggleEl){
1831                 return;
1832             }
1833
1834             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-minus']);
1835         }
1836         
1837     },
1838     
1839     collapse : function()
1840     {
1841         if(this.fireEvent('collapse', this)) {
1842             
1843             this.expanded = false;
1844             
1845             //this.el.select('.panel-body',true).first().setVisibilityMode(Roo.Element.DISPLAY).hide();
1846             this.el.select('.panel-body',true).first().addClass('hide');
1847         
1848             var toggleEl = this.toggleEl();
1849
1850             if(!toggleEl){
1851                 return;
1852             }
1853
1854             toggleEl.removeClass(['fa-minus', 'fa-plus']).addClass(['fa-plus']);
1855         }
1856     },
1857     
1858     toggleEl : function()
1859     {
1860         if(!this.el || !this.panel.length || !this.header.length || !this.expandable){
1861             return;
1862         }
1863         
1864         return this.el.select('.panel-heading .fa',true).first();
1865     },
1866     
1867     headerEl : function()
1868     {
1869         if(!this.el || !this.panel.length || !this.header.length){
1870             return;
1871         }
1872         
1873         return this.el.select('.panel-heading',true).first()
1874     },
1875     
1876     bodyEl : function()
1877     {
1878         if(!this.el || !this.panel.length){
1879             return;
1880         }
1881         
1882         return this.el.select('.panel-body',true).first()
1883     },
1884     
1885     titleEl : function()
1886     {
1887         if(!this.el || !this.panel.length || !this.header.length){
1888             return;
1889         }
1890         
1891         return this.el.select('.panel-title',true).first();
1892     },
1893     
1894     setTitle : function(v)
1895     {
1896         var titleEl = this.titleEl();
1897         
1898         if(!titleEl){
1899             return;
1900         }
1901         
1902         titleEl.dom.innerHTML = v;
1903     },
1904     
1905     getTitle : function()
1906     {
1907         
1908         var titleEl = this.titleEl();
1909         
1910         if(!titleEl){
1911             return '';
1912         }
1913         
1914         return titleEl.dom.innerHTML;
1915     },
1916     
1917     setRightTitle : function(v)
1918     {
1919         var t = this.el.select('.panel-header-right',true).first();
1920         
1921         if(!t){
1922             return;
1923         }
1924         
1925         t.dom.innerHTML = v;
1926     },
1927     
1928     onClick : function(e)
1929     {
1930         e.preventDefault();
1931         
1932         this.fireEvent('click', this, e);
1933     }
1934 });
1935
1936  /*
1937  *  - LGPL
1938  *
1939  *  This is BS4's Card element.. - similar to our containers probably..
1940  * 
1941  */
1942 /**
1943  * @class Roo.bootstrap.Card
1944  * @extends Roo.bootstrap.Component
1945  * Bootstrap Card class
1946  *
1947  *
1948  * possible... may not be implemented..
1949  * @cfg {String} header_image  src url of image.
1950  * @cfg {String|Object} header
1951  * @cfg {Number} header_size (0|1|2|3|4|5) H1 or H2 etc.. 0 indicates default
1952  * @cfg {Number} header_weight  (primary|secondary|success|info|warning|danger|light|dark)
1953  * 
1954  * @cfg {String} title
1955  * @cfg {String} subtitle
1956  * @cfg {String|Boolean} html -- html contents - or just use children.. use false to hide it..
1957  * @cfg {String} footer
1958  
1959  * @cfg {String} weight (primary|warning|info|danger|secondary|success|light|dark)
1960  * 
1961  * @cfg {String} margin (0|1|2|3|4|5|auto)
1962  * @cfg {String} margin_top (0|1|2|3|4|5|auto)
1963  * @cfg {String} margin_bottom (0|1|2|3|4|5|auto)
1964  * @cfg {String} margin_left (0|1|2|3|4|5|auto)
1965  * @cfg {String} margin_right (0|1|2|3|4|5|auto)
1966  * @cfg {String} margin_x (0|1|2|3|4|5|auto)
1967  * @cfg {String} margin_y (0|1|2|3|4|5|auto)
1968  *
1969  * @cfg {String} padding (0|1|2|3|4|5)
1970  * @cfg {String} padding_top (0|1|2|3|4|5)next_to_card
1971  * @cfg {String} padding_bottom (0|1|2|3|4|5)
1972  * @cfg {String} padding_left (0|1|2|3|4|5)
1973  * @cfg {String} padding_right (0|1|2|3|4|5)
1974  * @cfg {String} padding_x (0|1|2|3|4|5)
1975  * @cfg {String} padding_y (0|1|2|3|4|5)
1976  *
1977  * @cfg {String} display (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1978  * @cfg {String} display_xs (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1979  * @cfg {String} display_sm (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1980  * @cfg {String} display_lg (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1981  * @cfg {String} display_xl (none|inline|inline-block|block|table|table-cell|table-row|flex|inline-flex)
1982  
1983  * @config {Boolean} dragable  if this card can be dragged.
1984  * @config {String} drag_group  group for drag
1985  * @config {Boolean} dropable  if this card can recieve other cards being dropped onto it..
1986  * @config {String} drop_group  group for drag
1987  * 
1988  * @config {Boolean} collapsable can the body be collapsed.
1989  * @config {Boolean} collapsed is the body collapsed when rendered...
1990  * @config {Boolean} rotateable can the body be rotated by clicking on it..
1991  * @config {Boolean} rotated is the body rotated when rendered...
1992  * 
1993  * @constructor
1994  * Create a new Container
1995  * @param {Object} config The config object
1996  */
1997
1998 Roo.bootstrap.Card = function(config){
1999     Roo.bootstrap.Card.superclass.constructor.call(this, config);
2000     
2001     this.addEvents({
2002          // raw events
2003         /**
2004          * @event drop
2005          * When a element a card is dropped
2006          * @param {Roo.bootstrap.Card} this
2007          *
2008          * 
2009          * @param {Roo.bootstrap.Card} move_card the card being dropped?
2010          * @param {String} position 'above' or 'below'
2011          * @param {Roo.bootstrap.Card} next_to_card What card position is relative to of 'false' for empty list.
2012         
2013          */
2014         'drop' : true,
2015          /**
2016          * @event rotate
2017          * When a element a card is rotate
2018          * @param {Roo.bootstrap.Element} this
2019          * @param {Roo.Element} n the node being dropped?
2020          * @param {Boolean} rotate status
2021          */
2022         'rotate' : true
2023         
2024     });
2025 };
2026
2027
2028 Roo.extend(Roo.bootstrap.Card, Roo.bootstrap.Component,  {
2029     
2030     
2031     weight : '',
2032     
2033     margin: '', /// may be better in component?
2034     margin_top: '', 
2035     margin_bottom: '', 
2036     margin_left: '',
2037     margin_right: '',
2038     margin_x: '',
2039     margin_y: '',
2040     
2041     padding : '',
2042     padding_top: '', 
2043     padding_bottom: '', 
2044     padding_left: '',
2045     padding_right: '',
2046     padding_x: '',
2047     padding_y: '',
2048     
2049     display: '', 
2050     display_xs: '', 
2051     display_sm: '', 
2052     display_lg: '',
2053     display_xl: '',
2054  
2055     header_image  : '',
2056     header : '',
2057     header_size : 0,
2058     title : '',
2059     subtitle : '',
2060     html : '',
2061     footer: '',
2062
2063     collapsable : false,
2064     collapsed : false,
2065     rotateable : false,
2066     rotated : false,
2067     
2068     dragable : false,
2069     drag_group : false,
2070     dropable : false,
2071     drop_group : false,
2072     childContainer : false,
2073     dropEl : false, /// the dom placeholde element that indicates drop location.
2074     containerEl: false, // body container
2075     bodyEl: false, // card-body
2076     headerContainerEl : false, //
2077     headerEl : false,
2078     
2079     layoutCls : function()
2080     {
2081         var cls = '';
2082         var t = this;
2083         Roo.log(this.margin_bottom.length);
2084         ['', 'top', 'bottom', 'left', 'right', 'x', 'y' ].forEach(function(v) {
2085             // in theory these can do margin_top : ml-xs-3 ??? but we don't support that yet
2086             
2087             if (('' + t['margin' + (v.length ? '_' : '') + v]).length) {
2088                 cls += ' m' +  (v.length ? v[0]  : '') + '-' +  t['margin' + (v.length ? '_' : '') + v];
2089             }
2090             if (('' + t['padding' + (v.length ? '_' : '') + v]).length) {
2091                 cls += ' p' +  (v.length ? v[0]  : '') + '-' +  t['padding' + (v.length ? '_' : '') + v];
2092             }
2093         });
2094         
2095         ['', 'xs', 'sm', 'lg', 'xl'].forEach(function(v) {
2096             if (('' + t['display' + (v.length ? '_' : '') + v]).length) {
2097                 cls += ' d' +  (v.length ? '-' : '') + v + '-' + t['display' + (v.length ? '_' : '') + v]
2098             }
2099         });
2100         
2101         // more generic support?
2102         if (this.hidden) {
2103             cls += ' d-none';
2104         }
2105         
2106         return cls;
2107     },
2108  
2109        // Roo.log("Call onRender: " + this.xtype);
2110         /*  We are looking at something like this.
2111 <div class="card">
2112     <img src="..." class="card-img-top" alt="...">
2113     <div class="card-body">
2114         <h5 class="card-title">Card title</h5>
2115          <h6 class="card-subtitle mb-2 text-muted">Card subtitle</h6>
2116
2117         >> this bit is really the body...
2118         <div> << we will ad dthis in hopefully it will not break shit.
2119         
2120         ** card text does not actually have any styling...
2121         
2122             <p class="card-text">This is a wider card with supporting text below as a natural lead-in to additional content. This content is a little bit longer.</p>
2123         
2124         </div> <<
2125           <a href="#" class="card-link">Card link</a>
2126           
2127     </div>
2128     <div class="card-footer">
2129         <small class="text-muted">Last updated 3 mins ago</small>
2130     </div>
2131 </div>
2132          */
2133     getAutoCreate : function(){
2134         
2135         var cfg = {
2136             tag : 'div',
2137             cls : 'card',
2138             cn : [ ]
2139         };
2140         
2141         if (this.weight.length && this.weight != 'light') {
2142             cfg.cls += ' text-white';
2143         } else {
2144             cfg.cls += ' text-dark'; // need as it's nested..
2145         }
2146         if (this.weight.length) {
2147             cfg.cls += ' bg-' + this.weight;
2148         }
2149         
2150         cfg.cls += ' ' + this.layoutCls(); 
2151         
2152         var hdr = false;
2153         var hdr_ctr = false;
2154         if (this.header.length) {
2155             hdr = {
2156                 tag : this.header_size > 0 ? 'h' + this.header_size : 'div',
2157                 cls : 'card-header ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2158                 cn : []
2159             };
2160             cfg.cn.push(hdr);
2161             hdr_ctr = hdr;
2162         } else {
2163             hdr = {
2164                 tag : 'div',
2165                 cls : 'card-header d-none ' + (this.header_weight ? 'bg-' + this.header_weight : ''),
2166                 cn : []
2167             };
2168             cfg.cn.push(hdr);
2169             hdr_ctr = hdr;
2170         }
2171         if (this.collapsable) {
2172             hdr_ctr = {
2173             tag : 'a',
2174             cls : 'd-block user-select-none',
2175             cn: [
2176                     {
2177                         tag: 'i',
2178                         cls : 'roo-collapse-toggle fa fa-chevron-down float-right ' + (this.collapsed ? 'collapsed' : '')
2179                     }
2180                    
2181                 ]
2182             };
2183             hdr.cn.push(hdr_ctr);
2184         }
2185         
2186         hdr_ctr.cn.push(        {
2187             tag: 'span',
2188             cls: 'roo-card-header-ctr' + ( this.header.length ? '' : ' d-none'),
2189             html : this.header
2190         });
2191         
2192         
2193         if (this.header_image.length) {
2194             cfg.cn.push({
2195                 tag : 'img',
2196                 cls : 'card-img-top',
2197                 src: this.header_image // escape?
2198             });
2199         } else {
2200             cfg.cn.push({
2201                     tag : 'div',
2202                     cls : 'card-img-top d-none' 
2203                 });
2204         }
2205             
2206         var body = {
2207             tag : 'div',
2208             cls : 'card-body' + (this.html === false  ? ' d-none' : ''),
2209             cn : []
2210         };
2211         var obody = body;
2212         if (this.collapsable || this.rotateable) {
2213             obody = {
2214                 tag: 'div',
2215                 cls : 'roo-collapsable collapse ' + (this.collapsed || this.rotated ? '' : 'show'),
2216                 cn : [  body ]
2217             };
2218         }
2219         
2220         cfg.cn.push(obody);
2221         
2222         if (this.title.length) {
2223             body.cn.push({
2224                 tag : 'div',
2225                 cls : 'card-title',
2226                 src: this.title // escape?
2227             });
2228         }  
2229         
2230         if (this.subtitle.length) {
2231             body.cn.push({
2232                 tag : 'div',
2233                 cls : 'card-title',
2234                 src: this.subtitle // escape?
2235             });
2236         }
2237         
2238         body.cn.push({
2239             tag : 'div',
2240             cls : 'roo-card-body-ctr'
2241         });
2242         
2243         if (this.html.length) {
2244             body.cn.push({
2245                 tag: 'div',
2246                 html : this.html
2247             });
2248         }
2249         // fixme ? handle objects?
2250         
2251         if (this.footer.length) {
2252            
2253             cfg.cn.push({
2254                 cls : 'card-footer ' + (this.rotated ? 'd-none' : ''),
2255                 html : this.footer
2256             });
2257             
2258         } else {
2259             cfg.cn.push({cls : 'card-footer d-none'});
2260         }
2261         
2262         // footer...
2263         
2264         return cfg;
2265     },
2266     
2267     
2268     getCardHeader : function()
2269     {
2270         var  ret = this.el.select('.card-header',true).first();
2271         if (ret.hasClass('d-none')) {
2272             ret.removeClass('d-none');
2273         }
2274         
2275         return ret;
2276     },
2277     getCardFooter : function()
2278     {
2279         var  ret = this.el.select('.card-footer',true).first();
2280         if (ret.hasClass('d-none')) {
2281             ret.removeClass('d-none');
2282         }
2283         
2284         return ret;
2285     },
2286     getCardImageTop : function()
2287     {
2288         var  ret = this.el.select('.card-img-top',true).first();
2289         if (ret.hasClass('d-none')) {
2290             ret.removeClass('d-none');
2291         }
2292             
2293         return ret;
2294     },
2295     
2296     getChildContainer : function()
2297     {
2298         
2299         if(!this.el){
2300             return false;
2301         }
2302         return this.el.select('.roo-card-body-ctr',true).first();    
2303     },
2304     
2305     initEvents: function() 
2306     {
2307         this.bodyEl = this.el.select('.card-body',true).first(); 
2308         this.containerEl = this.getChildContainer();
2309         if(this.dragable){
2310             this.dragZone = new Roo.dd.DragZone(this.getEl(), {
2311                     containerScroll: true,
2312                     ddGroup: this.drag_group || 'default_card_drag_group'
2313             });
2314             this.dragZone.getDragData = this.getDragData.createDelegate(this);
2315         }
2316         if (this.dropable) {
2317             this.dropZone = new Roo.dd.DropZone(this.el.select('.card-body',true).first() , {
2318                 containerScroll: true,
2319                 ddGroup: this.drop_group || 'default_card_drag_group'
2320             });
2321             this.dropZone.getTargetFromEvent = this.getTargetFromEvent.createDelegate(this);
2322             this.dropZone.onNodeEnter = this.onNodeEnter.createDelegate(this);
2323             this.dropZone.onNodeOver = this.onNodeOver.createDelegate(this);
2324             this.dropZone.onNodeOut = this.onNodeOut.createDelegate(this);
2325             this.dropZone.onNodeDrop = this.onNodeDrop.createDelegate(this);
2326         }
2327         
2328         if (this.collapsable) {
2329             this.el.select('.card-header',true).on('click', this.onToggleCollapse, this);
2330         }
2331         if (this.rotateable) {
2332             this.el.select('.card-header',true).on('click', this.onToggleRotate, this);
2333         }
2334         this.collapsableEl = this.el.select('.roo-collapsable').first();
2335          
2336         this.footerEl = this.el.select('.card-footer').first();
2337         this.collapsableToggleEl = this.el.select('.roo-collapse-toggle');
2338         this.headerContainerEl = this.el.select('.roo-card-header-ctr').first();
2339         this.headerEl = this.el.select('.card-header',true).first();
2340         
2341         if (this.rotated) {
2342             this.el.addClass('roo-card-rotated');
2343             this.fireEvent('rotate', this, true);
2344         }
2345         
2346     },
2347     getDragData : function(e)
2348     {
2349         var target = this.getEl();
2350         if (target) {
2351             //this.handleSelection(e);
2352             
2353             var dragData = {
2354                 source: this,
2355                 copy: false,
2356                 nodes: this.getEl(),
2357                 records: []
2358             };
2359             
2360             
2361             dragData.ddel = target.dom ;    // the div element
2362             Roo.log(target.getWidth( ));
2363             dragData.ddel.style.width = target.getWidth() + 'px';
2364             
2365             return dragData;
2366         }
2367         return false;
2368     },
2369     /**
2370     *    Part of the Roo.dd.DropZone interface. If no target node is found, the
2371     *    whole Element becomes the target, and this causes the drop gesture to append.
2372     */
2373     getTargetFromEvent : function(e, dragged_card_el)
2374     {
2375         var target = e.getTarget();
2376         while ((target !== null) && (target.parentNode != this.containerEl.dom)) {
2377             target = target.parentNode;
2378         }
2379         
2380         var ret = {
2381             position: '',
2382             cards : [],
2383             card_n : -1,
2384             items_n : -1,
2385             card : false 
2386         };
2387         
2388         //Roo.log([ 'target' , target ? target.id : '--nothing--']);
2389         // see if target is one of the 'cards'...
2390         
2391         
2392         //Roo.log(this.items.length);
2393         var pos = false;
2394         
2395         var last_card_n = 0;
2396         var cards_len  = 0;
2397         for (var i = 0;i< this.items.length;i++) {
2398             
2399             if (!this.items[i].el.hasClass('card')) {
2400                  continue;
2401             }
2402             pos = this.getDropPoint(e, this.items[i].el.dom);
2403             
2404             cards_len = ret.cards.length;
2405             //Roo.log(this.items[i].el.dom.id);
2406             ret.cards.push(this.items[i]);
2407             last_card_n  = i;
2408             if (ret.card_n < 0 && pos == 'above') {
2409                 ret.position = cards_len > 0 ? 'below' : pos;
2410                 ret.items_n = i > 0 ? i - 1 : 0;
2411                 ret.card_n  = cards_len  > 0 ? cards_len - 1 : 0;
2412                 ret.card = ret.cards[ret.card_n];
2413             }
2414         }
2415         if (!ret.cards.length) {
2416             ret.card = true;
2417             ret.position = 'below';
2418             ret.items_n;
2419             return ret;
2420         }
2421         // could not find a card.. stick it at the end..
2422         if (ret.card_n < 0) {
2423             ret.card_n = last_card_n;
2424             ret.card = ret.cards[last_card_n];
2425             ret.items_n = this.items.indexOf(ret.cards[last_card_n]);
2426             ret.position = 'below';
2427         }
2428         
2429         if (this.items[ret.items_n].el == dragged_card_el) {
2430             return false;
2431         }
2432         
2433         if (ret.position == 'below') {
2434             var card_after = ret.card_n+1 == ret.cards.length ? false : ret.cards[ret.card_n+1];
2435             
2436             if (card_after  && card_after.el == dragged_card_el) {
2437                 return false;
2438             }
2439             return ret;
2440         }
2441         
2442         // its's after ..
2443         var card_before = ret.card_n > 0 ? ret.cards[ret.card_n-1] : false;
2444         
2445         if (card_before  && card_before.el == dragged_card_el) {
2446             return false;
2447         }
2448         
2449         return ret;
2450     },
2451     
2452     onNodeEnter : function(n, dd, e, data){
2453         return false;
2454     },
2455     onNodeOver : function(n, dd, e, data)
2456     {
2457        
2458         var target_info = this.getTargetFromEvent(e,data.source.el);
2459         if (target_info === false) {
2460             this.dropPlaceHolder('hide');
2461             return false;
2462         }
2463         Roo.log(['getTargetFromEvent', target_info ]);
2464         
2465          
2466         this.dropPlaceHolder('show', target_info,data);
2467         
2468         return false; 
2469     },
2470     onNodeOut : function(n, dd, e, data){
2471         this.dropPlaceHolder('hide');
2472      
2473     },
2474     onNodeDrop : function(n, dd, e, data)
2475     {
2476         
2477         // call drop - return false if
2478         
2479         // this could actually fail - if the Network drops..
2480         // we will ignore this at present..- client should probably reload
2481         // the whole set of cards if stuff like that fails.
2482         
2483         
2484         var info = this.getTargetFromEvent(e,data.source.el);
2485         if (info === false) {
2486             return false;
2487         }
2488         this.dropPlaceHolder('hide');
2489   
2490          
2491     
2492     
2493     
2494         this.acceptCard(data.source, info.position, info.card, info.items_n);
2495         return true;
2496          
2497     },
2498     firstChildCard : function()
2499     {
2500         for (var i = 0;i< this.items.length;i++) {
2501             
2502             if (!this.items[i].el.hasClass('card')) {
2503                  continue;
2504             }
2505             return this.items[i];
2506         }
2507         return this.items.length ? this.items[this.items.length-1] : false; // don't try and put stuff after the cards...
2508     },
2509     /**
2510      * accept card
2511      *
2512      * -        card.acceptCard(move_card, info.position, info.card, info.items_n);
2513      */
2514     acceptCard : function(move_card,  position, next_to_card )
2515     {
2516         if (this.fireEvent("drop", this, move_card, position, next_to_card) === false) {
2517             return false;
2518         }
2519         
2520         var to_items_n = next_to_card ? this.items.indexOf(next_to_card) : 0;
2521         
2522         move_card.parent().removeCard(move_card);
2523         
2524         
2525         var dom = move_card.el.dom;
2526         dom.style.width = ''; // clear with - which is set by drag.
2527         
2528         if (next_to_card !== false && next_to_card !== true && next_to_card.el.dom.parentNode) {
2529             var cardel = next_to_card.el.dom;
2530             
2531             if (position == 'above' ) {
2532                 cardel.parentNode.insertBefore(dom, cardel);
2533             } else if (cardel.nextSibling) {
2534                 cardel.parentNode.insertBefore(dom,cardel.nextSibling);
2535             } else {
2536                 cardel.parentNode.append(dom);
2537             }
2538         } else {
2539             // card container???
2540             this.containerEl.dom.append(dom);
2541         }
2542         
2543         //FIXME HANDLE card = true 
2544         
2545         // add this to the correct place in items.
2546         
2547         // remove Card from items.
2548         
2549        
2550         if (this.items.length) {
2551             var nitems = [];
2552             //Roo.log([info.items_n, info.position, this.items.length]);
2553             for (var i =0; i < this.items.length; i++) {
2554                 if (i == to_items_n && position == 'above') {
2555                     nitems.push(move_card);
2556                 }
2557                 nitems.push(this.items[i]);
2558                 if (i == to_items_n && position == 'below') {
2559                     nitems.push(move_card);
2560                 }
2561             }
2562             this.items = nitems;
2563             Roo.log(this.items);
2564         } else {
2565             this.items.push(move_card);
2566         }
2567         
2568         move_card.parentId = this.id;
2569         
2570         return true;
2571         
2572         
2573     },
2574     removeCard : function(c)
2575     {
2576         this.items = this.items.filter(function(e) { return e != c });
2577  
2578         var dom = c.el.dom;
2579         dom.parentNode.removeChild(dom);
2580         dom.style.width = ''; // clear with - which is set by drag.
2581         c.parentId = false;
2582         
2583     },
2584     
2585     /**    Decide whether to drop above or below a View node. */
2586     getDropPoint : function(e, n, dd)
2587     {
2588         if (dd) {
2589              return false;
2590         }
2591         if (n == this.containerEl.dom) {
2592             return "above";
2593         }
2594         var t = Roo.lib.Dom.getY(n), b = t + n.offsetHeight;
2595         var c = t + (b - t) / 2;
2596         var y = Roo.lib.Event.getPageY(e);
2597         if(y <= c) {
2598             return "above";
2599         }else{
2600             return "below";
2601         }
2602     },
2603     onToggleCollapse : function(e)
2604         {
2605         if (this.collapsed) {
2606             this.el.select('.roo-collapse-toggle').removeClass('collapsed');
2607             this.collapsableEl.addClass('show');
2608             this.collapsed = false;
2609             return;
2610         }
2611         this.el.select('.roo-collapse-toggle').addClass('collapsed');
2612         this.collapsableEl.removeClass('show');
2613         this.collapsed = true;
2614         
2615     
2616     },
2617     
2618     onToggleRotate : function(e)
2619     {
2620         this.collapsableEl.removeClass('show');
2621         this.footerEl.removeClass('d-none');
2622         this.el.removeClass('roo-card-rotated');
2623         this.el.removeClass('d-none');
2624         if (this.rotated) {
2625             
2626             this.collapsableEl.addClass('show');
2627             this.rotated = false;
2628             this.fireEvent('rotate', this, this.rotated);
2629             return;
2630         }
2631         this.el.addClass('roo-card-rotated');
2632         this.footerEl.addClass('d-none');
2633         this.el.select('.roo-collapsable').removeClass('show');
2634         
2635         this.rotated = true;
2636         this.fireEvent('rotate', this, this.rotated);
2637     
2638     },
2639     
2640     dropPlaceHolder: function (action, info, data)
2641     {
2642         if (this.dropEl === false) {
2643             this.dropEl = Roo.DomHelper.append(this.containerEl, {
2644             cls : 'd-none'
2645             },true);
2646         }
2647         this.dropEl.removeClass(['d-none', 'd-block']);        
2648         if (action == 'hide') {
2649             
2650             this.dropEl.addClass('d-none');
2651             return;
2652         }
2653         // FIXME - info.card == true!!!
2654         this.dropEl.dom.parentNode.removeChild(this.dropEl.dom);
2655         
2656         if (info.card !== true) {
2657             var cardel = info.card.el.dom;
2658             
2659             if (info.position == 'above') {
2660                 cardel.parentNode.insertBefore(this.dropEl.dom, cardel);
2661             } else if (cardel.nextSibling) {
2662                 cardel.parentNode.insertBefore(this.dropEl.dom,cardel.nextSibling);
2663             } else {
2664                 cardel.parentNode.append(this.dropEl.dom);
2665             }
2666         } else {
2667             // card container???
2668             this.containerEl.dom.append(this.dropEl.dom);
2669         }
2670         
2671         this.dropEl.addClass('d-block roo-card-dropzone');
2672         
2673         this.dropEl.setHeight( Roo.get(data.ddel).getHeight() );
2674         
2675         
2676     
2677     
2678     
2679     },
2680     setHeaderText: function(html)
2681     {
2682         this.headerContainerEl.dom.innerHTML = html;
2683     }
2684
2685     
2686 });
2687
2688 /*
2689  * - LGPL
2690  *
2691  * Card header - holder for the card header elements.
2692  * 
2693  */
2694
2695 /**
2696  * @class Roo.bootstrap.CardHeader
2697  * @extends Roo.bootstrap.Element
2698  * Bootstrap CardHeader class
2699  * @constructor
2700  * Create a new Card Header - that you can embed children into
2701  * @param {Object} config The config object
2702  */
2703
2704 Roo.bootstrap.CardHeader = function(config){
2705     Roo.bootstrap.CardHeader.superclass.constructor.call(this, config);
2706 };
2707
2708 Roo.extend(Roo.bootstrap.CardHeader, Roo.bootstrap.Element,  {
2709     
2710     
2711     container_method : 'getCardHeader' 
2712     
2713      
2714     
2715     
2716    
2717 });
2718
2719  
2720
2721  /*
2722  * - LGPL
2723  *
2724  * Card footer - holder for the card footer elements.
2725  * 
2726  */
2727
2728 /**
2729  * @class Roo.bootstrap.CardFooter
2730  * @extends Roo.bootstrap.Element
2731  * Bootstrap CardFooter class
2732  * @constructor
2733  * Create a new Card Footer - that you can embed children into
2734  * @param {Object} config The config object
2735  */
2736
2737 Roo.bootstrap.CardFooter = function(config){
2738     Roo.bootstrap.CardFooter.superclass.constructor.call(this, config);
2739 };
2740
2741 Roo.extend(Roo.bootstrap.CardFooter, Roo.bootstrap.Element,  {
2742     
2743     
2744     container_method : 'getCardFooter' 
2745     
2746      
2747     
2748     
2749    
2750 });
2751
2752  
2753
2754  /*
2755  * - LGPL
2756  *
2757  * Card header - holder for the card header elements.
2758  * 
2759  */
2760
2761 /**
2762  * @class Roo.bootstrap.CardImageTop
2763  * @extends Roo.bootstrap.Element
2764  * Bootstrap CardImageTop class
2765  * @constructor
2766  * Create a new Card Image Top container
2767  * @param {Object} config The config object
2768  */
2769
2770 Roo.bootstrap.CardImageTop = function(config){
2771     Roo.bootstrap.CardImageTop.superclass.constructor.call(this, config);
2772 };
2773
2774 Roo.extend(Roo.bootstrap.CardImageTop, Roo.bootstrap.Element,  {
2775     
2776    
2777     container_method : 'getCardImageTop' 
2778     
2779      
2780     
2781    
2782 });
2783
2784  
2785
2786  /*
2787  * - LGPL
2788  *
2789  * image
2790  * 
2791  */
2792
2793
2794 /**
2795  * @class Roo.bootstrap.Img
2796  * @extends Roo.bootstrap.Component
2797  * Bootstrap Img class
2798  * @cfg {Boolean} imgResponsive false | true
2799  * @cfg {String} border rounded | circle | thumbnail
2800  * @cfg {String} src image source
2801  * @cfg {String} alt image alternative text
2802  * @cfg {String} href a tag href
2803  * @cfg {String} target (_self|_blank|_parent|_top)target for a href.
2804  * @cfg {String} xsUrl xs image source
2805  * @cfg {String} smUrl sm image source
2806  * @cfg {String} mdUrl md image source
2807  * @cfg {String} lgUrl lg image source
2808  * 
2809  * @constructor
2810  * Create a new Input
2811  * @param {Object} config The config object
2812  */
2813
2814 Roo.bootstrap.Img = function(config){
2815     Roo.bootstrap.Img.superclass.constructor.call(this, config);
2816     
2817     this.addEvents({
2818         // img events
2819         /**
2820          * @event click
2821          * The img click event for the img.
2822          * @param {Roo.EventObject} e
2823          */
2824         "click" : true
2825     });
2826 };
2827
2828 Roo.extend(Roo.bootstrap.Img, Roo.bootstrap.Component,  {
2829     
2830     imgResponsive: true,
2831     border: '',
2832     src: 'about:blank',
2833     href: false,
2834     target: false,
2835     xsUrl: '',
2836     smUrl: '',
2837     mdUrl: '',
2838     lgUrl: '',
2839
2840     getAutoCreate : function()
2841     {   
2842         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2843             return this.createSingleImg();
2844         }
2845         
2846         var cfg = {
2847             tag: 'div',
2848             cls: 'roo-image-responsive-group',
2849             cn: []
2850         };
2851         var _this = this;
2852         
2853         Roo.each(['xs', 'sm', 'md', 'lg'], function(size){
2854             
2855             if(!_this[size + 'Url']){
2856                 return;
2857             }
2858             
2859             var img = {
2860                 tag: 'img',
2861                 cls: (_this.imgResponsive) ? 'img-responsive' : '',
2862                 html: _this.html || cfg.html,
2863                 src: _this[size + 'Url']
2864             };
2865             
2866             img.cls += ' roo-image-responsive-' + size;
2867             
2868             var s = ['xs', 'sm', 'md', 'lg'];
2869             
2870             s.splice(s.indexOf(size), 1);
2871             
2872             Roo.each(s, function(ss){
2873                 img.cls += ' hidden-' + ss;
2874             });
2875             
2876             if (['rounded','circle','thumbnail'].indexOf(_this.border)>-1) {
2877                 cfg.cls += ' img-' + _this.border;
2878             }
2879             
2880             if(_this.alt){
2881                 cfg.alt = _this.alt;
2882             }
2883             
2884             if(_this.href){
2885                 var a = {
2886                     tag: 'a',
2887                     href: _this.href,
2888                     cn: [
2889                         img
2890                     ]
2891                 };
2892
2893                 if(this.target){
2894                     a.target = _this.target;
2895                 }
2896             }
2897             
2898             cfg.cn.push((_this.href) ? a : img);
2899             
2900         });
2901         
2902         return cfg;
2903     },
2904     
2905     createSingleImg : function()
2906     {
2907         var cfg = {
2908             tag: 'img',
2909             cls: (this.imgResponsive) ? 'img-responsive' : '',
2910             html : null,
2911             src : 'about:blank'  // just incase src get's set to undefined?!?
2912         };
2913         
2914         cfg.html = this.html || cfg.html;
2915         
2916         cfg.src = this.src || cfg.src;
2917         
2918         if (['rounded','circle','thumbnail'].indexOf(this.border)>-1) {
2919             cfg.cls += ' img-' + this.border;
2920         }
2921         
2922         if(this.alt){
2923             cfg.alt = this.alt;
2924         }
2925         
2926         if(this.href){
2927             var a = {
2928                 tag: 'a',
2929                 href: this.href,
2930                 cn: [
2931                     cfg
2932                 ]
2933             };
2934             
2935             if(this.target){
2936                 a.target = this.target;
2937             }
2938             
2939         }
2940         
2941         return (this.href) ? a : cfg;
2942     },
2943     
2944     initEvents: function() 
2945     {
2946         if(!this.href){
2947             this.el.on('click', this.onClick, this);
2948         }
2949         
2950     },
2951     
2952     onClick : function(e)
2953     {
2954         Roo.log('img onclick');
2955         this.fireEvent('click', this, e);
2956     },
2957     /**
2958      * Sets the url of the image - used to update it
2959      * @param {String} url the url of the image
2960      */
2961     
2962     setSrc : function(url)
2963     {
2964         this.src =  url;
2965         
2966         if(this.src || (!this.xsUrl && !this.smUrl && !this.mdUrl && !this.lgUrl)){
2967             this.el.dom.src =  url;
2968             return;
2969         }
2970         
2971         this.el.select('img', true).first().dom.src =  url;
2972     }
2973     
2974     
2975    
2976 });
2977
2978  /*
2979  * - LGPL
2980  *
2981  * image
2982  * 
2983  */
2984
2985
2986 /**
2987  * @class Roo.bootstrap.Link
2988  * @extends Roo.bootstrap.Component
2989  * Bootstrap Link Class
2990  * @cfg {String} alt image alternative text
2991  * @cfg {String} href a tag href
2992  * @cfg {String} target (_self|_blank|_parent|_top) target for a href.
2993  * @cfg {String} html the content of the link.
2994  * @cfg {String} anchor name for the anchor link
2995  * @cfg {String} fa - favicon
2996
2997  * @cfg {Boolean} preventDefault (true | false) default false
2998
2999  * 
3000  * @constructor
3001  * Create a new Input
3002  * @param {Object} config The config object
3003  */
3004
3005 Roo.bootstrap.Link = function(config){
3006     Roo.bootstrap.Link.superclass.constructor.call(this, config);
3007     
3008     this.addEvents({
3009         // img events
3010         /**
3011          * @event click
3012          * The img click event for the img.
3013          * @param {Roo.EventObject} e
3014          */
3015         "click" : true
3016     });
3017 };
3018
3019 Roo.extend(Roo.bootstrap.Link, Roo.bootstrap.Component,  {
3020     
3021     href: false,
3022     target: false,
3023     preventDefault: false,
3024     anchor : false,
3025     alt : false,
3026     fa: false,
3027
3028
3029     getAutoCreate : function()
3030     {
3031         var html = this.html || '';
3032         
3033         if (this.fa !== false) {
3034             html = '<i class="fa fa-' + this.fa + '"></i>';
3035         }
3036         var cfg = {
3037             tag: 'a'
3038         };
3039         // anchor's do not require html/href...
3040         if (this.anchor === false) {
3041             cfg.html = html;
3042             cfg.href = this.href || '#';
3043         } else {
3044             cfg.name = this.anchor;
3045             if (this.html !== false || this.fa !== false) {
3046                 cfg.html = html;
3047             }
3048             if (this.href !== false) {
3049                 cfg.href = this.href;
3050             }
3051         }
3052         
3053         if(this.alt !== false){
3054             cfg.alt = this.alt;
3055         }
3056         
3057         
3058         if(this.target !== false) {
3059             cfg.target = this.target;
3060         }
3061         
3062         return cfg;
3063     },
3064     
3065     initEvents: function() {
3066         
3067         if(!this.href || this.preventDefault){
3068             this.el.on('click', this.onClick, this);
3069         }
3070     },
3071     
3072     onClick : function(e)
3073     {
3074         if(this.preventDefault){
3075             e.preventDefault();
3076         }
3077         //Roo.log('img onclick');
3078         this.fireEvent('click', this, e);
3079     }
3080    
3081 });
3082
3083  /*
3084  * - LGPL
3085  *
3086  * header
3087  * 
3088  */
3089
3090 /**
3091  * @class Roo.bootstrap.Header
3092  * @extends Roo.bootstrap.Component
3093  * Bootstrap Header class
3094  * @cfg {String} html content of header
3095  * @cfg {Number} level (1|2|3|4|5|6) default 1
3096  * 
3097  * @constructor
3098  * Create a new Header
3099  * @param {Object} config The config object
3100  */
3101
3102
3103 Roo.bootstrap.Header  = function(config){
3104     Roo.bootstrap.Header.superclass.constructor.call(this, config);
3105 };
3106
3107 Roo.extend(Roo.bootstrap.Header, Roo.bootstrap.Component,  {
3108     
3109     //href : false,
3110     html : false,
3111     level : 1,
3112     
3113     
3114     
3115     getAutoCreate : function(){
3116         
3117         
3118         
3119         var cfg = {
3120             tag: 'h' + (1 *this.level),
3121             html: this.html || ''
3122         } ;
3123         
3124         return cfg;
3125     }
3126    
3127 });
3128
3129  
3130
3131  /*
3132  * Based on:
3133  * Ext JS Library 1.1.1
3134  * Copyright(c) 2006-2007, Ext JS, LLC.
3135  *
3136  * Originally Released Under LGPL - original licence link has changed is not relivant.
3137  *
3138  * Fork - LGPL
3139  * <script type="text/javascript">
3140  */
3141  
3142 /**
3143  * @class Roo.bootstrap.MenuMgr
3144  * Provides a common registry of all menu items on a page so that they can be easily accessed by id.
3145  * @singleton
3146  */
3147 Roo.bootstrap.MenuMgr = function(){
3148    var menus, active, groups = {}, attached = false, lastShow = new Date();
3149
3150    // private - called when first menu is created
3151    function init(){
3152        menus = {};
3153        active = new Roo.util.MixedCollection();
3154        Roo.get(document).addKeyListener(27, function(){
3155            if(active.length > 0){
3156                hideAll();
3157            }
3158        });
3159    }
3160
3161    // private
3162    function hideAll(){
3163        if(active && active.length > 0){
3164            var c = active.clone();
3165            c.each(function(m){
3166                m.hide();
3167            });
3168        }
3169    }
3170
3171    // private
3172    function onHide(m){
3173        active.remove(m);
3174        if(active.length < 1){
3175            Roo.get(document).un("mouseup", onMouseDown);
3176             
3177            attached = false;
3178        }
3179    }
3180
3181    // private
3182    function onShow(m){
3183        var last = active.last();
3184        lastShow = new Date();
3185        active.add(m);
3186        if(!attached){
3187           Roo.get(document).on("mouseup", onMouseDown);
3188            
3189            attached = true;
3190        }
3191        if(m.parentMenu){
3192           //m.getEl().setZIndex(parseInt(m.parentMenu.getEl().getStyle("z-index"), 10) + 3);
3193           m.parentMenu.activeChild = m;
3194        }else if(last && last.isVisible()){
3195           //m.getEl().setZIndex(parseInt(last.getEl().getStyle("z-index"), 10) + 3);
3196        }
3197    }
3198
3199    // private
3200    function onBeforeHide(m){
3201        if(m.activeChild){
3202            m.activeChild.hide();
3203        }
3204        if(m.autoHideTimer){
3205            clearTimeout(m.autoHideTimer);
3206            delete m.autoHideTimer;
3207        }
3208    }
3209
3210    // private
3211    function onBeforeShow(m){
3212        var pm = m.parentMenu;
3213        if(!pm && !m.allowOtherMenus){
3214            hideAll();
3215        }else if(pm && pm.activeChild && active != m){
3216            pm.activeChild.hide();
3217        }
3218    }
3219
3220    // private this should really trigger on mouseup..
3221    function onMouseDown(e){
3222         Roo.log("on Mouse Up");
3223         
3224         if(lastShow.getElapsed() > 50 && active.length > 0 && !e.getTarget(".dropdown-menu") && !e.getTarget('.user-menu')){
3225             Roo.log("MenuManager hideAll");
3226             hideAll();
3227             e.stopEvent();
3228         }
3229         
3230         
3231    }
3232
3233    // private
3234    function onBeforeCheck(mi, state){
3235        if(state){
3236            var g = groups[mi.group];
3237            for(var i = 0, l = g.length; i < l; i++){
3238                if(g[i] != mi){
3239                    g[i].setChecked(false);
3240                }
3241            }
3242        }
3243    }
3244
3245    return {
3246
3247        /**
3248         * Hides all menus that are currently visible
3249         */
3250        hideAll : function(){
3251             hideAll();  
3252        },
3253
3254        // private
3255        register : function(menu){
3256            if(!menus){
3257                init();
3258            }
3259            menus[menu.id] = menu;
3260            menu.on("beforehide", onBeforeHide);
3261            menu.on("hide", onHide);
3262            menu.on("beforeshow", onBeforeShow);
3263            menu.on("show", onShow);
3264            var g = menu.group;
3265            if(g && menu.events["checkchange"]){
3266                if(!groups[g]){
3267                    groups[g] = [];
3268                }
3269                groups[g].push(menu);
3270                menu.on("checkchange", onCheck);
3271            }
3272        },
3273
3274         /**
3275          * Returns a {@link Roo.menu.Menu} object
3276          * @param {String/Object} menu The string menu id, an existing menu object reference, or a Menu config that will
3277          * be used to generate and return a new Menu instance.
3278          */
3279        get : function(menu){
3280            if(typeof menu == "string"){ // menu id
3281                return menus[menu];
3282            }else if(menu.events){  // menu instance
3283                return menu;
3284            }
3285            /*else if(typeof menu.length == 'number'){ // array of menu items?
3286                return new Roo.bootstrap.Menu({items:menu});
3287            }else{ // otherwise, must be a config
3288                return new Roo.bootstrap.Menu(menu);
3289            }
3290            */
3291            return false;
3292        },
3293
3294        // private
3295        unregister : function(menu){
3296            delete menus[menu.id];
3297            menu.un("beforehide", onBeforeHide);
3298            menu.un("hide", onHide);
3299            menu.un("beforeshow", onBeforeShow);
3300            menu.un("show", onShow);
3301            var g = menu.group;
3302            if(g && menu.events["checkchange"]){
3303                groups[g].remove(menu);
3304                menu.un("checkchange", onCheck);
3305            }
3306        },
3307
3308        // private
3309        registerCheckable : function(menuItem){
3310            var g = menuItem.group;
3311            if(g){
3312                if(!groups[g]){
3313                    groups[g] = [];
3314                }
3315                groups[g].push(menuItem);
3316                menuItem.on("beforecheckchange", onBeforeCheck);
3317            }
3318        },
3319
3320        // private
3321        unregisterCheckable : function(menuItem){
3322            var g = menuItem.group;
3323            if(g){
3324                groups[g].remove(menuItem);
3325                menuItem.un("beforecheckchange", onBeforeCheck);
3326            }
3327        }
3328    };
3329 }();/*
3330  * - LGPL
3331  *
3332  * menu
3333  * 
3334  */
3335
3336 /**
3337  * @class Roo.bootstrap.Menu
3338  * @extends Roo.bootstrap.Component
3339  * Bootstrap Menu class - container for MenuItems
3340  * @cfg {String} type (dropdown|treeview|submenu) type of menu
3341  * @cfg {bool} hidden  if the menu should be hidden when rendered.
3342  * @cfg {bool} stopEvent (true|false)  Stop event after trigger press (default true)
3343  * @cfg {bool} isLink (true|false)  the menu has link disable auto expand and collaspe (default false)
3344  * 
3345  * @constructor
3346  * Create a new Menu
3347  * @param {Object} config The config object
3348  */
3349
3350
3351 Roo.bootstrap.Menu = function(config){
3352     Roo.bootstrap.Menu.superclass.constructor.call(this, config);
3353     if (this.registerMenu && this.type != 'treeview')  {
3354         Roo.bootstrap.MenuMgr.register(this);
3355     }
3356     
3357     
3358     this.addEvents({
3359         /**
3360          * @event beforeshow
3361          * Fires before this menu is displayed (return false to block)
3362          * @param {Roo.menu.Menu} this
3363          */
3364         beforeshow : true,
3365         /**
3366          * @event beforehide
3367          * Fires before this menu is hidden (return false to block)
3368          * @param {Roo.menu.Menu} this
3369          */
3370         beforehide : true,
3371         /**
3372          * @event show
3373          * Fires after this menu is displayed
3374          * @param {Roo.menu.Menu} this
3375          */
3376         show : true,
3377         /**
3378          * @event hide
3379          * Fires after this menu is hidden
3380          * @param {Roo.menu.Menu} this
3381          */
3382         hide : true,
3383         /**
3384          * @event click
3385          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
3386          * @param {Roo.menu.Menu} this
3387          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3388          * @param {Roo.EventObject} e
3389          */
3390         click : true,
3391         /**
3392          * @event mouseover
3393          * Fires when the mouse is hovering over this menu
3394          * @param {Roo.menu.Menu} this
3395          * @param {Roo.EventObject} e
3396          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3397          */
3398         mouseover : true,
3399         /**
3400          * @event mouseout
3401          * Fires when the mouse exits this menu
3402          * @param {Roo.menu.Menu} this
3403          * @param {Roo.EventObject} e
3404          * @param {Roo.menu.Item} menuItem The menu item that was clicked
3405          */
3406         mouseout : true,
3407         /**
3408          * @event itemclick
3409          * Fires when a menu item contained in this menu is clicked
3410          * @param {Roo.menu.BaseItem} baseItem The BaseItem that was clicked
3411          * @param {Roo.EventObject} e
3412          */
3413         itemclick: true
3414     });
3415     this.menuitems = new Roo.util.MixedCollection(false, function(o) { return o.el.id; });
3416 };
3417
3418 Roo.extend(Roo.bootstrap.Menu, Roo.bootstrap.Component,  {
3419     
3420    /// html : false,
3421     //align : '',
3422     triggerEl : false,  // is this set by component builder? -- it should really be fetched from parent()???
3423     type: false,
3424     /**
3425      * @cfg {Boolean} registerMenu True (default) - means that clicking on screen etc. hides it.
3426      */
3427     registerMenu : true,
3428     
3429     menuItems :false, // stores the menu items..
3430     
3431     hidden:true,
3432         
3433     parentMenu : false,
3434     
3435     stopEvent : true,
3436     
3437     isLink : false,
3438     
3439     getChildContainer : function() {
3440         return this.el;  
3441     },
3442     
3443     getAutoCreate : function(){
3444          
3445         //if (['right'].indexOf(this.align)!==-1) {
3446         //    cfg.cn[1].cls += ' pull-right'
3447         //}
3448         
3449         
3450         var cfg = {
3451             tag : 'ul',
3452             cls : 'dropdown-menu' ,
3453             style : 'z-index:1000'
3454             
3455         };
3456         
3457         if (this.type === 'submenu') {
3458             cfg.cls = 'submenu active';
3459         }
3460         if (this.type === 'treeview') {
3461             cfg.cls = 'treeview-menu';
3462         }
3463         
3464         return cfg;
3465     },
3466     initEvents : function() {
3467         
3468        // Roo.log("ADD event");
3469        // Roo.log(this.triggerEl.dom);
3470         
3471         this.triggerEl.on('click', this.onTriggerClick, this);
3472         
3473         this.triggerEl.on(Roo.isTouch ? 'touchstart' : 'mouseup', this.onTriggerPress, this);
3474         
3475         
3476         if (this.triggerEl.hasClass('nav-item')) {
3477             // dropdown toggle on the 'a' in BS4?
3478             this.triggerEl.select('.nav-link',true).first().addClass('dropdown-toggle');
3479         } else {
3480             this.triggerEl.addClass('dropdown-toggle');
3481         }
3482         if (Roo.isTouch) {
3483             this.el.on('touchstart'  , this.onTouch, this);
3484         }
3485         this.el.on('click' , this.onClick, this);
3486
3487         this.el.on("mouseover", this.onMouseOver, this);
3488         this.el.on("mouseout", this.onMouseOut, this);
3489         
3490     },
3491     
3492     findTargetItem : function(e)
3493     {
3494         var t = e.getTarget(".dropdown-menu-item", this.el,  true);
3495         if(!t){
3496             return false;
3497         }
3498         //Roo.log(t);         Roo.log(t.id);
3499         if(t && t.id){
3500             //Roo.log(this.menuitems);
3501             return this.menuitems.get(t.id);
3502             
3503             //return this.items.get(t.menuItemId);
3504         }
3505         
3506         return false;
3507     },
3508     
3509     onTouch : function(e) 
3510     {
3511         Roo.log("menu.onTouch");
3512         //e.stopEvent(); this make the user popdown broken
3513         this.onClick(e);
3514     },
3515     
3516     onClick : function(e)
3517     {
3518         Roo.log("menu.onClick");
3519         
3520         var t = this.findTargetItem(e);
3521         if(!t || t.isContainer){
3522             return;
3523         }
3524         Roo.log(e);
3525         /*
3526         if (Roo.isTouch && e.type == 'touchstart' && t.menu  && !t.disabled) {
3527             if(t == this.activeItem && t.shouldDeactivate(e)){
3528                 this.activeItem.deactivate();
3529                 delete this.activeItem;
3530                 return;
3531             }
3532             if(t.canActivate){
3533                 this.setActiveItem(t, true);
3534             }
3535             return;
3536             
3537             
3538         }
3539         */
3540        
3541         Roo.log('pass click event');
3542         
3543         t.onClick(e);
3544         
3545         this.fireEvent("click", this, t, e);
3546         
3547         var _this = this;
3548         
3549         if(!t.href.length || t.href == '#'){
3550             (function() { _this.hide(); }).defer(100);
3551         }
3552         
3553     },
3554     
3555     onMouseOver : function(e){
3556         var t  = this.findTargetItem(e);
3557         //Roo.log(t);
3558         //if(t){
3559         //    if(t.canActivate && !t.disabled){
3560         //        this.setActiveItem(t, true);
3561         //    }
3562         //}
3563         
3564         this.fireEvent("mouseover", this, e, t);
3565     },
3566     isVisible : function(){
3567         return !this.hidden;
3568     },
3569     onMouseOut : function(e){
3570         var t  = this.findTargetItem(e);
3571         
3572         //if(t ){
3573         //    if(t == this.activeItem && t.shouldDeactivate(e)){
3574         //        this.activeItem.deactivate();
3575         //        delete this.activeItem;
3576         //    }
3577         //}
3578         this.fireEvent("mouseout", this, e, t);
3579     },
3580     
3581     
3582     /**
3583      * Displays this menu relative to another element
3584      * @param {String/HTMLElement/Roo.Element} element The element to align to
3585      * @param {String} position (optional) The {@link Roo.Element#alignTo} anchor position to use in aligning to
3586      * the element (defaults to this.defaultAlign)
3587      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3588      */
3589     show : function(el, pos, parentMenu)
3590     {
3591         if (false === this.fireEvent("beforeshow", this)) {
3592             Roo.log("show canceled");
3593             return;
3594         }
3595         this.parentMenu = parentMenu;
3596         if(!this.el){
3597             this.render();
3598         }
3599         
3600         this.showAt(this.el.getAlignToXY(el, pos || this.defaultAlign), parentMenu, false);
3601     },
3602      /**
3603      * Displays this menu at a specific xy position
3604      * @param {Array} xyPosition Contains X & Y [x, y] values for the position at which to show the menu (coordinates are page-based)
3605      * @param {Roo.menu.Menu} parentMenu (optional) This menu's parent menu, if applicable (defaults to undefined)
3606      */
3607     showAt : function(xy, parentMenu, /* private: */_e){
3608         this.parentMenu = parentMenu;
3609         if(!this.el){
3610             this.render();
3611         }
3612         if(_e !== false){
3613             this.fireEvent("beforeshow", this);
3614             //xy = this.el.adjustForConstraints(xy);
3615         }
3616         
3617         //this.el.show();
3618         this.hideMenuItems();
3619         this.hidden = false;
3620         this.triggerEl.addClass('open');
3621         this.el.addClass('show');
3622         
3623         // reassign x when hitting right
3624         if(this.el.getWidth() + xy[0] >= Roo.lib.Dom.getViewWidth()){
3625             xy[0] = xy[0] - this.el.getWidth() + this.triggerEl.getWidth();
3626         }
3627         
3628         // reassign y when hitting bottom
3629         if(this.el.getHeight() + xy[1] >= Roo.lib.Dom.getViewHeight()){
3630             xy[1] = xy[1] - this.el.getHeight() - this.triggerEl.getHeight();
3631         }
3632         
3633         // but the list may align on trigger left or trigger top... should it be a properity?
3634         
3635         if(this.el.getStyle('top') != 'auto' && this.el.getStyle('top').slice(-1) != "%"){
3636             this.el.setXY(xy);
3637         }
3638         
3639         this.focus();
3640         this.fireEvent("show", this);
3641     },
3642     
3643     focus : function(){
3644         return;
3645         if(!this.hidden){
3646             this.doFocus.defer(50, this);
3647         }
3648     },
3649
3650     doFocus : function(){
3651         if(!this.hidden){
3652             this.focusEl.focus();
3653         }
3654     },
3655
3656     /**
3657      * Hides this menu and optionally all parent menus
3658      * @param {Boolean} deep (optional) True to hide all parent menus recursively, if any (defaults to false)
3659      */
3660     hide : function(deep)
3661     {
3662         if (false === this.fireEvent("beforehide", this)) {
3663             Roo.log("hide canceled");
3664             return;
3665         }
3666         this.hideMenuItems();
3667         if(this.el && this.isVisible()){
3668            
3669             if(this.activeItem){
3670                 this.activeItem.deactivate();
3671                 this.activeItem = null;
3672             }
3673             this.triggerEl.removeClass('open');;
3674             this.el.removeClass('show');
3675             this.hidden = true;
3676             this.fireEvent("hide", this);
3677         }
3678         if(deep === true && this.parentMenu){
3679             this.parentMenu.hide(true);
3680         }
3681     },
3682     
3683     onTriggerClick : function(e)
3684     {
3685         Roo.log('trigger click');
3686         
3687         var target = e.getTarget();
3688         
3689         Roo.log(target.nodeName.toLowerCase());
3690         
3691         if(target.nodeName.toLowerCase() === 'i'){
3692             e.preventDefault();
3693         }
3694         
3695     },
3696     
3697     onTriggerPress  : function(e)
3698     {
3699         Roo.log('trigger press');
3700         //Roo.log(e.getTarget());
3701        // Roo.log(this.triggerEl.dom);
3702        
3703         // trigger only occurs on normal menu's -- if it's a treeview or dropdown... do not hide/show..
3704         var pel = Roo.get(e.getTarget());
3705         if (pel.findParent('.dropdown-menu') || pel.findParent('.treeview-menu') ) {
3706             Roo.log('is treeview or dropdown?');
3707             return;
3708         }
3709         
3710         if(e.getTarget().nodeName.toLowerCase() !== 'i' && this.isLink){
3711             return;
3712         }
3713         
3714         if (this.isVisible()) {
3715             Roo.log('hide');
3716             this.hide();
3717         } else {
3718             Roo.log('show');
3719             this.show(this.triggerEl, '?', false);
3720         }
3721         
3722         if(this.stopEvent || e.getTarget().nodeName.toLowerCase() === 'i'){
3723             e.stopEvent();
3724         }
3725         
3726     },
3727        
3728     
3729     hideMenuItems : function()
3730     {
3731         Roo.log("hide Menu Items");
3732         if (!this.el) { 
3733             return;
3734         }
3735         
3736         this.el.select('.open',true).each(function(aa) {
3737             
3738             aa.removeClass('open');
3739          
3740         });
3741     },
3742     addxtypeChild : function (tree, cntr) {
3743         var comp= Roo.bootstrap.Menu.superclass.addxtypeChild.call(this, tree, cntr);
3744           
3745         this.menuitems.add(comp);
3746         return comp;
3747
3748     },
3749     getEl : function()
3750     {
3751         Roo.log(this.el);
3752         return this.el;
3753     },
3754     
3755     clear : function()
3756     {
3757         this.getEl().dom.innerHTML = '';
3758         this.menuitems.clear();
3759     }
3760 });
3761
3762  
3763  /*
3764  * - LGPL
3765  *
3766  * menu item
3767  * 
3768  */
3769
3770
3771 /**
3772  * @class Roo.bootstrap.MenuItem
3773  * @extends Roo.bootstrap.Component
3774  * Bootstrap MenuItem class
3775  * @cfg {String} html the menu label
3776  * @cfg {String} href the link
3777  * @cfg {Boolean} preventDefault do not trigger A href on clicks (default false).
3778  * @cfg {Boolean} isContainer is it a container - just returns a drop down item..
3779  * @cfg {Boolean} active  used on sidebars to highlight active itesm
3780  * @cfg {String} fa favicon to show on left of menu item.
3781  * @cfg {Roo.bootsrap.Menu} menu the child menu.
3782  * 
3783  * 
3784  * @constructor
3785  * Create a new MenuItem
3786  * @param {Object} config The config object
3787  */
3788
3789
3790 Roo.bootstrap.MenuItem = function(config){
3791     Roo.bootstrap.MenuItem.superclass.constructor.call(this, config);
3792     this.addEvents({
3793         // raw events
3794         /**
3795          * @event click
3796          * The raw click event for the entire grid.
3797          * @param {Roo.bootstrap.MenuItem} this
3798          * @param {Roo.EventObject} e
3799          */
3800         "click" : true
3801     });
3802 };
3803
3804 Roo.extend(Roo.bootstrap.MenuItem, Roo.bootstrap.Component,  {
3805     
3806     href : false,
3807     html : false,
3808     preventDefault: false,
3809     isContainer : false,
3810     active : false,
3811     fa: false,
3812     
3813     getAutoCreate : function(){
3814         
3815         if(this.isContainer){
3816             return {
3817                 tag: 'li',
3818                 cls: 'dropdown-menu-item '
3819             };
3820         }
3821         var ctag = {
3822             tag: 'span',
3823             html: 'Link'
3824         };
3825         
3826         var anc = {
3827             tag : 'a',
3828             cls : 'dropdown-item',
3829             href : '#',
3830             cn : [  ]
3831         };
3832         
3833         if (this.fa !== false) {
3834             anc.cn.push({
3835                 tag : 'i',
3836                 cls : 'fa fa-' + this.fa
3837             });
3838         }
3839         
3840         anc.cn.push(ctag);
3841         
3842         
3843         var cfg= {
3844             tag: 'li',
3845             cls: 'dropdown-menu-item',
3846             cn: [ anc ]
3847         };
3848         if (this.parent().type == 'treeview') {
3849             cfg.cls = 'treeview-menu';
3850         }
3851         if (this.active) {
3852             cfg.cls += ' active';
3853         }
3854         
3855         
3856         
3857         anc.href = this.href || cfg.cn[0].href ;
3858         ctag.html = this.html || cfg.cn[0].html ;
3859         return cfg;
3860     },
3861     
3862     initEvents: function()
3863     {
3864         if (this.parent().type == 'treeview') {
3865             this.el.select('a').on('click', this.onClick, this);
3866         }
3867         
3868         if (this.menu) {
3869             this.menu.parentType = this.xtype;
3870             this.menu.triggerEl = this.el;
3871             this.menu = this.addxtype(Roo.apply({}, this.menu));
3872         }
3873         
3874     },
3875     onClick : function(e)
3876     {
3877         Roo.log('item on click ');
3878         
3879         if(this.preventDefault){
3880             e.preventDefault();
3881         }
3882         //this.parent().hideMenuItems();
3883         
3884         this.fireEvent('click', this, e);
3885     },
3886     getEl : function()
3887     {
3888         return this.el;
3889     } 
3890 });
3891
3892  
3893
3894  /*
3895  * - LGPL
3896  *
3897  * menu separator
3898  * 
3899  */
3900
3901
3902 /**
3903  * @class Roo.bootstrap.MenuSeparator
3904  * @extends Roo.bootstrap.Component
3905  * Bootstrap MenuSeparator class
3906  * 
3907  * @constructor
3908  * Create a new MenuItem
3909  * @param {Object} config The config object
3910  */
3911
3912
3913 Roo.bootstrap.MenuSeparator = function(config){
3914     Roo.bootstrap.MenuSeparator.superclass.constructor.call(this, config);
3915 };
3916
3917 Roo.extend(Roo.bootstrap.MenuSeparator, Roo.bootstrap.Component,  {
3918     
3919     getAutoCreate : function(){
3920         var cfg = {
3921             cls: 'divider',
3922             tag : 'li'
3923         };
3924         
3925         return cfg;
3926     }
3927    
3928 });
3929
3930  
3931
3932  
3933 /*
3934 * Licence: LGPL
3935 */
3936
3937 /**
3938  * @class Roo.bootstrap.Modal
3939  * @extends Roo.bootstrap.Component
3940  * Bootstrap Modal class
3941  * @cfg {String} title Title of dialog
3942  * @cfg {String} html - the body of the dialog (for simple ones) - you can also use template..
3943  * @cfg {Roo.Template} tmpl - a template with variables. to use it, add a handler in show:method  adn
3944  * @cfg {Boolean} specificTitle default false
3945  * @cfg {Array} buttons Array of buttons or standard button set..
3946  * @cfg {String} buttonPosition (left|right|center) default right (DEPRICATED) - use mr-auto on buttons to put them on the left
3947  * @cfg {Boolean} animate default true
3948  * @cfg {Boolean} allow_close default true
3949  * @cfg {Boolean} fitwindow default false
3950  * @cfg {Number} width fixed width - usefull for chrome extension only really.
3951  * @cfg {Number} height fixed height - usefull for chrome extension only really.
3952  * @cfg {String} size (sm|lg|xl) default empty
3953  * @cfg {Number} max_width set the max width of modal
3954  * @cfg {Boolean} editableTitle can the title be edited
3955
3956  *
3957  *
3958  * @constructor
3959  * Create a new Modal Dialog
3960  * @param {Object} config The config object
3961  */
3962
3963 Roo.bootstrap.Modal = function(config){
3964     Roo.bootstrap.Modal.superclass.constructor.call(this, config);
3965     this.addEvents({
3966         // raw events
3967         /**
3968          * @event btnclick
3969          * The raw btnclick event for the button
3970          * @param {Roo.EventObject} e
3971          */
3972         "btnclick" : true,
3973         /**
3974          * @event resize
3975          * Fire when dialog resize
3976          * @param {Roo.bootstrap.Modal} this
3977          * @param {Roo.EventObject} e
3978          */
3979         "resize" : true,
3980         /**
3981          * @event titlechanged
3982          * Fire when the editable title has been changed
3983          * @param {Roo.bootstrap.Modal} this
3984          * @param {Roo.EventObject} value
3985          */
3986         "titlechanged" : true 
3987         
3988     });
3989     this.buttons = this.buttons || [];
3990
3991     if (this.tmpl) {
3992         this.tmpl = Roo.factory(this.tmpl);
3993     }
3994
3995 };
3996
3997 Roo.extend(Roo.bootstrap.Modal, Roo.bootstrap.Component,  {
3998
3999     title : 'test dialog',
4000
4001     buttons : false,
4002
4003     // set on load...
4004
4005     html: false,
4006
4007     tmp: false,
4008
4009     specificTitle: false,
4010
4011     buttonPosition: 'right',
4012
4013     allow_close : true,
4014
4015     animate : true,
4016
4017     fitwindow: false,
4018     
4019      // private
4020     dialogEl: false,
4021     bodyEl:  false,
4022     footerEl:  false,
4023     titleEl:  false,
4024     closeEl:  false,
4025
4026     size: '',
4027     
4028     max_width: 0,
4029     
4030     max_height: 0,
4031     
4032     fit_content: false,
4033     editableTitle  : false,
4034
4035     onRender : function(ct, position)
4036     {
4037         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
4038
4039         if(!this.el){
4040             var cfg = Roo.apply({},  this.getAutoCreate());
4041             cfg.id = Roo.id();
4042             //if(!cfg.name){
4043             //    cfg.name = typeof(this.name) == 'undefined' ? this.id : this.name;
4044             //}
4045             //if (!cfg.name.length) {
4046             //    delete cfg.name;
4047            // }
4048             if (this.cls) {
4049                 cfg.cls += ' ' + this.cls;
4050             }
4051             if (this.style) {
4052                 cfg.style = this.style;
4053             }
4054             this.el = Roo.get(document.body).createChild(cfg, position);
4055         }
4056         //var type = this.el.dom.type;
4057
4058
4059         if(this.tabIndex !== undefined){
4060             this.el.dom.setAttribute('tabIndex', this.tabIndex);
4061         }
4062
4063         this.dialogEl = this.el.select('.modal-dialog',true).first();
4064         this.bodyEl = this.el.select('.modal-body',true).first();
4065         this.closeEl = this.el.select('.modal-header .close', true).first();
4066         this.headerEl = this.el.select('.modal-header',true).first();
4067         this.titleEl = this.el.select('.modal-title',true).first();
4068         this.footerEl = this.el.select('.modal-footer',true).first();
4069
4070         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
4071         
4072         //this.el.addClass("x-dlg-modal");
4073
4074         if (this.buttons.length) {
4075             Roo.each(this.buttons, function(bb) {
4076                 var b = Roo.apply({}, bb);
4077                 b.xns = b.xns || Roo.bootstrap;
4078                 b.xtype = b.xtype || 'Button';
4079                 if (typeof(b.listeners) == 'undefined') {
4080                     b.listeners = { click : this.onButtonClick.createDelegate(this)  };
4081                 }
4082
4083                 var btn = Roo.factory(b);
4084
4085                 btn.render(this.getButtonContainer());
4086
4087             },this);
4088         }
4089         // render the children.
4090         var nitems = [];
4091
4092         if(typeof(this.items) != 'undefined'){
4093             var items = this.items;
4094             delete this.items;
4095
4096             for(var i =0;i < items.length;i++) {
4097                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
4098             }
4099         }
4100
4101         this.items = nitems;
4102
4103         // where are these used - they used to be body/close/footer
4104
4105
4106         this.initEvents();
4107         //this.el.addClass([this.fieldClass, this.cls]);
4108
4109     },
4110
4111     getAutoCreate : function()
4112     {
4113         // we will default to modal-body-overflow - might need to remove or make optional later.
4114         var bdy = {
4115                 cls : 'modal-body ' + (this.fitwindow ? 'overflow-auto' : ''), 
4116                 html : this.html || ''
4117         };
4118
4119         var title = {
4120             tag: 'h4',
4121             cls : 'modal-title',
4122             html : this.title
4123         };
4124
4125         if(this.specificTitle){ // WTF is this?
4126             title = this.title;
4127         }
4128
4129         var header = [];
4130         if (this.allow_close && Roo.bootstrap.version == 3) {
4131             header.push({
4132                 tag: 'button',
4133                 cls : 'close',
4134                 html : '&times'
4135             });
4136         }
4137
4138         header.push(title);
4139
4140         if (this.editableTitle) {
4141             header.push({
4142                 cls: 'form-control roo-editable-title d-none',
4143                 tag: 'input',
4144                 type: 'text'
4145             });
4146         }
4147         
4148         if (this.allow_close && Roo.bootstrap.version == 4) {
4149             header.push({
4150                 tag: 'button',
4151                 cls : 'close',
4152                 html : '&times'
4153             });
4154         }
4155         
4156         var size = '';
4157
4158         if(this.size.length){
4159             size = 'modal-' + this.size;
4160         }
4161         
4162         var footer = Roo.bootstrap.version == 3 ?
4163             {
4164                 cls : 'modal-footer',
4165                 cn : [
4166                     {
4167                         tag: 'div',
4168                         cls: 'btn-' + this.buttonPosition
4169                     }
4170                 ]
4171
4172             } :
4173             {  // BS4 uses mr-auto on left buttons....
4174                 cls : 'modal-footer'
4175             };
4176
4177             
4178
4179         
4180         
4181         var modal = {
4182             cls: "modal",
4183              cn : [
4184                 {
4185                     cls: "modal-dialog " + size,
4186                     cn : [
4187                         {
4188                             cls : "modal-content",
4189                             cn : [
4190                                 {
4191                                     cls : 'modal-header',
4192                                     cn : header
4193                                 },
4194                                 bdy,
4195                                 footer
4196                             ]
4197
4198                         }
4199                     ]
4200
4201                 }
4202             ]
4203         };
4204
4205         if(this.animate){
4206             modal.cls += ' fade';
4207         }
4208
4209         return modal;
4210
4211     },
4212     getChildContainer : function() {
4213
4214          return this.bodyEl;
4215
4216     },
4217     getButtonContainer : function() {
4218         
4219          return Roo.bootstrap.version == 4 ?
4220             this.el.select('.modal-footer',true).first()
4221             : this.el.select('.modal-footer div',true).first();
4222
4223     },
4224     initEvents : function()
4225     {
4226         if (this.allow_close) {
4227             this.closeEl.on('click', this.hide, this);
4228         }
4229         Roo.EventManager.onWindowResize(this.resize, this, true);
4230         if (this.editableTitle) {
4231             this.headerEditEl =  this.headerEl.select('.form-control',true).first();
4232             this.headerEl.on('click', function() { this.toggleHeaderInput(true) } , this);
4233             this.headerEditEl.on('keyup', function(e) {
4234                     if([  e.RETURN , e.TAB , e.ESC ].indexOf(e.keyCode) > -1) {
4235                         this.toggleHeaderInput(false)
4236                     }
4237                 }, this);
4238             this.headerEditEl.on('blur', function(e) {
4239                 this.toggleHeaderInput(false)
4240             },this);
4241         }
4242
4243     },
4244   
4245
4246     resize : function()
4247     {
4248         this.maskEl.setSize(
4249             Roo.lib.Dom.getViewWidth(true),
4250             Roo.lib.Dom.getViewHeight(true)
4251         );
4252         
4253         if (this.fitwindow) {
4254             
4255            
4256             this.setSize(
4257                 this.width || Roo.lib.Dom.getViewportWidth(true) - 30,
4258                 this.height || Roo.lib.Dom.getViewportHeight(true) // catering margin-top 30 margin-bottom 30
4259             );
4260             return;
4261         }
4262         
4263         if(this.max_width !== 0) {
4264             
4265             var w = Math.min(this.max_width, Roo.lib.Dom.getViewportWidth(true) - 30);
4266             
4267             if(this.height) {
4268                 this.setSize(w, this.height);
4269                 return;
4270             }
4271             
4272             if(this.max_height) {
4273                 this.setSize(w,Math.min(
4274                     this.max_height,
4275                     Roo.lib.Dom.getViewportHeight(true) - 60
4276                 ));
4277                 
4278                 return;
4279             }
4280             
4281             if(!this.fit_content) {
4282                 this.setSize(w, Roo.lib.Dom.getViewportHeight(true) - 60);
4283                 return;
4284             }
4285             
4286             this.setSize(w, Math.min(
4287                 60 +
4288                 this.headerEl.getHeight() + 
4289                 this.footerEl.getHeight() + 
4290                 this.getChildHeight(this.bodyEl.dom.childNodes),
4291                 Roo.lib.Dom.getViewportHeight(true) - 60)
4292             );
4293         }
4294         
4295     },
4296
4297     setSize : function(w,h)
4298     {
4299         if (!w && !h) {
4300             return;
4301         }
4302         
4303         this.resizeTo(w,h);
4304     },
4305
4306     show : function() {
4307
4308         if (!this.rendered) {
4309             this.render();
4310         }
4311         this.toggleHeaderInput(false);
4312         //this.el.setStyle('display', 'block');
4313         this.el.removeClass('hideing');
4314         this.el.dom.style.display='block';
4315         
4316         Roo.get(document.body).addClass('modal-open');
4317  
4318         if(this.animate){  // element has 'fade'  - so stuff happens after .3s ?- not sure why the delay?
4319             
4320             (function(){
4321                 this.el.addClass('show');
4322                 this.el.addClass('in');
4323             }).defer(50, this);
4324         }else{
4325             this.el.addClass('show');
4326             this.el.addClass('in');
4327         }
4328
4329         // not sure how we can show data in here..
4330         //if (this.tmpl) {
4331         //    this.getChildContainer().dom.innerHTML = this.tmpl.applyTemplate(this);
4332         //}
4333
4334         Roo.get(document.body).addClass("x-body-masked");
4335         
4336         this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
4337         this.maskEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4338         this.maskEl.dom.style.display = 'block';
4339         this.maskEl.addClass('show');
4340         
4341         
4342         this.resize();
4343         
4344         this.fireEvent('show', this);
4345
4346         // set zindex here - otherwise it appears to be ignored...
4347         this.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
4348
4349         (function () {
4350             this.items.forEach( function(e) {
4351                 e.layout ? e.layout() : false;
4352
4353             });
4354         }).defer(100,this);
4355
4356     },
4357     hide : function()
4358     {
4359         if(this.fireEvent("beforehide", this) !== false){
4360             
4361             this.maskEl.removeClass('show');
4362             
4363             this.maskEl.dom.style.display = '';
4364             Roo.get(document.body).removeClass("x-body-masked");
4365             this.el.removeClass('in');
4366             this.el.select('.modal-dialog', true).first().setStyle('transform','');
4367
4368             if(this.animate){ // why
4369                 this.el.addClass('hideing');
4370                 this.el.removeClass('show');
4371                 (function(){
4372                     if (!this.el.hasClass('hideing')) {
4373                         return; // it's been shown again...
4374                     }
4375                     
4376                     this.el.dom.style.display='';
4377
4378                     Roo.get(document.body).removeClass('modal-open');
4379                     this.el.removeClass('hideing');
4380                 }).defer(150,this);
4381                 
4382             }else{
4383                 this.el.removeClass('show');
4384                 this.el.dom.style.display='';
4385                 Roo.get(document.body).removeClass('modal-open');
4386
4387             }
4388             this.fireEvent('hide', this);
4389         }
4390     },
4391     isVisible : function()
4392     {
4393         
4394         return this.el.hasClass('show') && !this.el.hasClass('hideing');
4395         
4396     },
4397
4398     addButton : function(str, cb)
4399     {
4400
4401
4402         var b = Roo.apply({}, { html : str } );
4403         b.xns = b.xns || Roo.bootstrap;
4404         b.xtype = b.xtype || 'Button';
4405         if (typeof(b.listeners) == 'undefined') {
4406             b.listeners = { click : cb.createDelegate(this)  };
4407         }
4408
4409         var btn = Roo.factory(b);
4410
4411         btn.render(this.getButtonContainer());
4412
4413         return btn;
4414
4415     },
4416
4417     setDefaultButton : function(btn)
4418     {
4419         //this.el.select('.modal-footer').()
4420     },
4421
4422     resizeTo: function(w,h)
4423     {
4424         this.dialogEl.setWidth(w);
4425         
4426         var diff = this.headerEl.getHeight() + this.footerEl.getHeight() + 60; // dialog margin-bottom: 30  
4427
4428         this.bodyEl.setHeight(h - diff);
4429         
4430         this.fireEvent('resize', this);
4431     },
4432     
4433     setContentSize  : function(w, h)
4434     {
4435
4436     },
4437     onButtonClick: function(btn,e)
4438     {
4439         //Roo.log([a,b,c]);
4440         this.fireEvent('btnclick', btn.name, e);
4441     },
4442      /**
4443      * Set the title of the Dialog
4444      * @param {String} str new Title
4445      */
4446     setTitle: function(str) {
4447         this.titleEl.dom.innerHTML = str;
4448         this.title = str;
4449     },
4450     /**
4451      * Set the body of the Dialog
4452      * @param {String} str new Title
4453      */
4454     setBody: function(str) {
4455         this.bodyEl.dom.innerHTML = str;
4456     },
4457     /**
4458      * Set the body of the Dialog using the template
4459      * @param {Obj} data - apply this data to the template and replace the body contents.
4460      */
4461     applyBody: function(obj)
4462     {
4463         if (!this.tmpl) {
4464             Roo.log("Error - using apply Body without a template");
4465             //code
4466         }
4467         this.tmpl.overwrite(this.bodyEl, obj);
4468     },
4469     
4470     getChildHeight : function(child_nodes)
4471     {
4472         if(
4473             !child_nodes ||
4474             child_nodes.length == 0
4475         ) {
4476             return 0;
4477         }
4478         
4479         var child_height = 0;
4480         
4481         for(var i = 0; i < child_nodes.length; i++) {
4482             
4483             /*
4484             * for modal with tabs...
4485             if(child_nodes[i].classList.contains('roo-layout-panel')) {
4486                 
4487                 var layout_childs = child_nodes[i].childNodes;
4488                 
4489                 for(var j = 0; j < layout_childs.length; j++) {
4490                     
4491                     if(layout_childs[j].classList.contains('roo-layout-panel-body')) {
4492                         
4493                         var layout_body_childs = layout_childs[j].childNodes;
4494                         
4495                         for(var k = 0; k < layout_body_childs.length; k++) {
4496                             
4497                             if(layout_body_childs[k].classList.contains('navbar')) {
4498                                 child_height += layout_body_childs[k].offsetHeight;
4499                                 continue;
4500                             }
4501                             
4502                             if(layout_body_childs[k].classList.contains('roo-layout-tabs-body')) {
4503                                 
4504                                 var layout_body_tab_childs = layout_body_childs[k].childNodes;
4505                                 
4506                                 for(var m = 0; m < layout_body_tab_childs.length; m++) {
4507                                     
4508                                     if(layout_body_tab_childs[m].classList.contains('roo-layout-active-content')) {
4509                                         child_height += this.getChildHeight(layout_body_tab_childs[m].childNodes);
4510                                         continue;
4511                                     }
4512                                     
4513                                 }
4514                                 
4515                             }
4516                             
4517                         }
4518                     }
4519                 }
4520                 continue;
4521             }
4522             */
4523             
4524             child_height += child_nodes[i].offsetHeight;
4525             // Roo.log(child_nodes[i].offsetHeight);
4526         }
4527         
4528         return child_height;
4529     },
4530     toggleHeaderInput : function(is_edit)
4531     {
4532         if (!this.editableTitle) {
4533             return; // not editable.
4534         }
4535         if (is_edit && this.is_header_editing) {
4536             return; // already editing..
4537         }
4538         if (is_edit) {
4539     
4540             this.headerEditEl.dom.value = this.title;
4541             this.headerEditEl.removeClass('d-none');
4542             this.headerEditEl.dom.focus();
4543             this.titleEl.addClass('d-none');
4544             
4545             this.is_header_editing = true;
4546             return
4547         }
4548         // flip back to not editing.
4549         this.title = this.headerEditEl.dom.value;
4550         this.headerEditEl.addClass('d-none');
4551         this.titleEl.removeClass('d-none');
4552         this.titleEl.dom.innerHTML = String.format('{0}', this.title);
4553         this.is_header_editing = false;
4554         this.fireEvent('titlechanged', this, this.title);
4555     
4556             
4557         
4558     }
4559
4560 });
4561
4562
4563 Roo.apply(Roo.bootstrap.Modal,  {
4564     /**
4565          * Button config that displays a single OK button
4566          * @type Object
4567          */
4568         OK :  [{
4569             name : 'ok',
4570             weight : 'primary',
4571             html : 'OK'
4572         }],
4573         /**
4574          * Button config that displays Yes and No buttons
4575          * @type Object
4576          */
4577         YESNO : [
4578             {
4579                 name  : 'no',
4580                 html : 'No'
4581             },
4582             {
4583                 name  :'yes',
4584                 weight : 'primary',
4585                 html : 'Yes'
4586             }
4587         ],
4588
4589         /**
4590          * Button config that displays OK and Cancel buttons
4591          * @type Object
4592          */
4593         OKCANCEL : [
4594             {
4595                name : 'cancel',
4596                 html : 'Cancel'
4597             },
4598             {
4599                 name : 'ok',
4600                 weight : 'primary',
4601                 html : 'OK'
4602             }
4603         ],
4604         /**
4605          * Button config that displays Yes, No and Cancel buttons
4606          * @type Object
4607          */
4608         YESNOCANCEL : [
4609             {
4610                 name : 'yes',
4611                 weight : 'primary',
4612                 html : 'Yes'
4613             },
4614             {
4615                 name : 'no',
4616                 html : 'No'
4617             },
4618             {
4619                 name : 'cancel',
4620                 html : 'Cancel'
4621             }
4622         ],
4623         
4624         zIndex : 10001
4625 });
4626
4627 /*
4628  * - LGPL
4629  *
4630  * messagebox - can be used as a replace
4631  * 
4632  */
4633 /**
4634  * @class Roo.MessageBox
4635  * Utility class for generating different styles of message boxes.  The alias Roo.Msg can also be used.
4636  * Example usage:
4637  *<pre><code>
4638 // Basic alert:
4639 Roo.Msg.alert('Status', 'Changes saved successfully.');
4640
4641 // Prompt for user data:
4642 Roo.Msg.prompt('Name', 'Please enter your name:', function(btn, text){
4643     if (btn == 'ok'){
4644         // process text value...
4645     }
4646 });
4647
4648 // Show a dialog using config options:
4649 Roo.Msg.show({
4650    title:'Save Changes?',
4651    msg: 'Your are closing a tab that has unsaved changes. Would you like to save your changes?',
4652    buttons: Roo.Msg.YESNOCANCEL,
4653    fn: processResult,
4654    animEl: 'elId'
4655 });
4656 </code></pre>
4657  * @singleton
4658  */
4659 Roo.bootstrap.MessageBox = function(){
4660     var dlg, opt, mask, waitTimer;
4661     var bodyEl, msgEl, textboxEl, textareaEl, progressEl, pp;
4662     var buttons, activeTextEl, bwidth;
4663
4664     
4665     // private
4666     var handleButton = function(button){
4667         dlg.hide();
4668         Roo.callback(opt.fn, opt.scope||window, [button, activeTextEl.dom.value], 1);
4669     };
4670
4671     // private
4672     var handleHide = function(){
4673         if(opt && opt.cls){
4674             dlg.el.removeClass(opt.cls);
4675         }
4676         //if(waitTimer){
4677         //    Roo.TaskMgr.stop(waitTimer);
4678         //    waitTimer = null;
4679         //}
4680     };
4681
4682     // private
4683     var updateButtons = function(b){
4684         var width = 0;
4685         if(!b){
4686             buttons["ok"].hide();
4687             buttons["cancel"].hide();
4688             buttons["yes"].hide();
4689             buttons["no"].hide();
4690             dlg.footerEl.hide();
4691             
4692             return width;
4693         }
4694         dlg.footerEl.show();
4695         for(var k in buttons){
4696             if(typeof buttons[k] != "function"){
4697                 if(b[k]){
4698                     buttons[k].show();
4699                     buttons[k].setText(typeof b[k] == "string" ? b[k] : Roo.bootstrap.MessageBox.buttonText[k]);
4700                     width += buttons[k].el.getWidth()+15;
4701                 }else{
4702                     buttons[k].hide();
4703                 }
4704             }
4705         }
4706         return width;
4707     };
4708
4709     // private
4710     var handleEsc = function(d, k, e){
4711         if(opt && opt.closable !== false){
4712             dlg.hide();
4713         }
4714         if(e){
4715             e.stopEvent();
4716         }
4717     };
4718
4719     return {
4720         /**
4721          * Returns a reference to the underlying {@link Roo.BasicDialog} element
4722          * @return {Roo.BasicDialog} The BasicDialog element
4723          */
4724         getDialog : function(){
4725            if(!dlg){
4726                 dlg = new Roo.bootstrap.Modal( {
4727                     //draggable: true,
4728                     //resizable:false,
4729                     //constraintoviewport:false,
4730                     //fixedcenter:true,
4731                     //collapsible : false,
4732                     //shim:true,
4733                     //modal: true,
4734                 //    width: 'auto',
4735                   //  height:100,
4736                     //buttonAlign:"center",
4737                     closeClick : function(){
4738                         if(opt && opt.buttons && opt.buttons.no && !opt.buttons.cancel){
4739                             handleButton("no");
4740                         }else{
4741                             handleButton("cancel");
4742                         }
4743                     }
4744                 });
4745                 dlg.render();
4746                 dlg.on("hide", handleHide);
4747                 mask = dlg.mask;
4748                 //dlg.addKeyListener(27, handleEsc);
4749                 buttons = {};
4750                 this.buttons = buttons;
4751                 var bt = this.buttonText;
4752                 buttons["ok"] = dlg.addButton(bt["ok"], handleButton.createCallback("ok"));
4753                 buttons["yes"] = dlg.addButton(bt["yes"], handleButton.createCallback("yes"));
4754                 buttons["no"] = dlg.addButton(bt["no"], handleButton.createCallback("no"));
4755                 buttons["cancel"] = dlg.addButton(bt["cancel"], handleButton.createCallback("cancel"));
4756                 //Roo.log(buttons);
4757                 bodyEl = dlg.bodyEl.createChild({
4758
4759                     html:'<span class="roo-mb-text"></span><br /><input type="text" class="roo-mb-input" />' +
4760                         '<textarea class="roo-mb-textarea"></textarea>' +
4761                         '<div class="roo-mb-progress-wrap"><div class="roo-mb-progress"><div class="roo-mb-progress-bar">&#160;</div></div></div>'
4762                 });
4763                 msgEl = bodyEl.dom.firstChild;
4764                 textboxEl = Roo.get(bodyEl.dom.childNodes[2]);
4765                 textboxEl.enableDisplayMode();
4766                 textboxEl.addKeyListener([10,13], function(){
4767                     if(dlg.isVisible() && opt && opt.buttons){
4768                         if(opt.buttons.ok){
4769                             handleButton("ok");
4770                         }else if(opt.buttons.yes){
4771                             handleButton("yes");
4772                         }
4773                     }
4774                 });
4775                 textareaEl = Roo.get(bodyEl.dom.childNodes[3]);
4776                 textareaEl.enableDisplayMode();
4777                 progressEl = Roo.get(bodyEl.dom.childNodes[4]);
4778                 progressEl.enableDisplayMode();
4779                 
4780                 // This is supposed to be the progessElement.. but I think it's controlling the height of everything..
4781                 var pf = progressEl.dom.firstChild;
4782                 if (pf) {
4783                     pp = Roo.get(pf.firstChild);
4784                     pp.setHeight(pf.offsetHeight);
4785                 }
4786                 
4787             }
4788             return dlg;
4789         },
4790
4791         /**
4792          * Updates the message box body text
4793          * @param {String} text (optional) Replaces the message box element's innerHTML with the specified string (defaults to
4794          * the XHTML-compliant non-breaking space character '&amp;#160;')
4795          * @return {Roo.MessageBox} This message box
4796          */
4797         updateText : function(text)
4798         {
4799             if(!dlg.isVisible() && !opt.width){
4800                 dlg.dialogEl.setStyle({ 'max-width' : this.maxWidth});
4801                 // dlg.resizeTo(this.maxWidth, 100); // forcing the height breaks long alerts()
4802             }
4803             msgEl.innerHTML = text || '&#160;';
4804       
4805             var cw =  Math.max(msgEl.offsetWidth, msgEl.parentNode.scrollWidth);
4806             //Roo.log("guesed size: " + JSON.stringify([cw,msgEl.offsetWidth, msgEl.parentNode.scrollWidth]));
4807             var w = Math.max(
4808                     Math.min(opt.width || cw , this.maxWidth), 
4809                     Math.max(opt.minWidth || this.minWidth, bwidth)
4810             );
4811             if(opt.prompt){
4812                 activeTextEl.setWidth(w);
4813             }
4814             if(dlg.isVisible()){
4815                 dlg.fixedcenter = false;
4816             }
4817             // to big, make it scroll. = But as usual stupid IE does not support
4818             // !important..
4819             
4820             if ( bodyEl.getHeight() > (Roo.lib.Dom.getViewHeight() - 100)) {
4821                 bodyEl.setHeight ( Roo.lib.Dom.getViewHeight() - 100 );
4822                 bodyEl.dom.style.overflowY = 'auto' + ( Roo.isIE ? '' : ' !important');
4823             } else {
4824                 bodyEl.dom.style.height = '';
4825                 bodyEl.dom.style.overflowY = '';
4826             }
4827             if (cw > w) {
4828                 bodyEl.dom.style.get = 'auto' + ( Roo.isIE ? '' : ' !important');
4829             } else {
4830                 bodyEl.dom.style.overflowX = '';
4831             }
4832             
4833             dlg.setContentSize(w, bodyEl.getHeight());
4834             if(dlg.isVisible()){
4835                 dlg.fixedcenter = true;
4836             }
4837             return this;
4838         },
4839
4840         /**
4841          * Updates a progress-style message box's text and progress bar.  Only relevant on message boxes
4842          * initiated via {@link Roo.MessageBox#progress} or by calling {@link Roo.MessageBox#show} with progress: true.
4843          * @param {Number} value Any number between 0 and 1 (e.g., .5)
4844          * @param {String} text (optional) If defined, the message box's body text is replaced with the specified string (defaults to undefined)
4845          * @return {Roo.MessageBox} This message box
4846          */
4847         updateProgress : function(value, text){
4848             if(text){
4849                 this.updateText(text);
4850             }
4851             
4852             if (pp) { // weird bug on my firefox - for some reason this is not defined
4853                 pp.setWidth(Math.floor(value*progressEl.dom.firstChild.offsetWidth));
4854                 pp.setHeight(Math.floor(progressEl.dom.firstChild.offsetHeight));
4855             }
4856             return this;
4857         },        
4858
4859         /**
4860          * Returns true if the message box is currently displayed
4861          * @return {Boolean} True if the message box is visible, else false
4862          */
4863         isVisible : function(){
4864             return dlg && dlg.isVisible();  
4865         },
4866
4867         /**
4868          * Hides the message box if it is displayed
4869          */
4870         hide : function(){
4871             if(this.isVisible()){
4872                 dlg.hide();
4873             }  
4874         },
4875
4876         /**
4877          * Displays a new message box, or reinitializes an existing message box, based on the config options
4878          * passed in. All functions (e.g. prompt, alert, etc) on MessageBox call this function internally.
4879          * The following config object properties are supported:
4880          * <pre>
4881 Property    Type             Description
4882 ----------  ---------------  ------------------------------------------------------------------------------------
4883 animEl            String/Element   An id or Element from which the message box should animate as it opens and
4884                                    closes (defaults to undefined)
4885 buttons           Object/Boolean   A button config object (e.g., Roo.MessageBox.OKCANCEL or {ok:'Foo',
4886                                    cancel:'Bar'}), or false to not show any buttons (defaults to false)
4887 closable          Boolean          False to hide the top-right close button (defaults to true).  Note that
4888                                    progress and wait dialogs will ignore this property and always hide the
4889                                    close button as they can only be closed programmatically.
4890 cls               String           A custom CSS class to apply to the message box element
4891 defaultTextHeight Number           The default height in pixels of the message box's multiline textarea if
4892                                    displayed (defaults to 75)
4893 fn                Function         A callback function to execute after closing the dialog.  The arguments to the
4894                                    function will be btn (the name of the button that was clicked, if applicable,
4895                                    e.g. "ok"), and text (the value of the active text field, if applicable).
4896                                    Progress and wait dialogs will ignore this option since they do not respond to
4897                                    user actions and can only be closed programmatically, so any required function
4898                                    should be called by the same code after it closes the dialog.
4899 icon              String           A CSS class that provides a background image to be used as an icon for
4900                                    the dialog (e.g., Roo.MessageBox.WARNING or 'custom-class', defaults to '')
4901 maxWidth          Number           The maximum width in pixels of the message box (defaults to 600)
4902 minWidth          Number           The minimum width in pixels of the message box (defaults to 100)
4903 modal             Boolean          False to allow user interaction with the page while the message box is
4904                                    displayed (defaults to true)
4905 msg               String           A string that will replace the existing message box body text (defaults
4906                                    to the XHTML-compliant non-breaking space character '&#160;')
4907 multiline         Boolean          True to prompt the user to enter multi-line text (defaults to false)
4908 progress          Boolean          True to display a progress bar (defaults to false)
4909 progressText      String           The text to display inside the progress bar if progress = true (defaults to '')
4910 prompt            Boolean          True to prompt the user to enter single-line text (defaults to false)
4911 proxyDrag         Boolean          True to display a lightweight proxy while dragging (defaults to false)
4912 title             String           The title text
4913 value             String           The string value to set into the active textbox element if displayed
4914 wait              Boolean          True to display a progress bar (defaults to false)
4915 width             Number           The width of the dialog in pixels
4916 </pre>
4917          *
4918          * Example usage:
4919          * <pre><code>
4920 Roo.Msg.show({
4921    title: 'Address',
4922    msg: 'Please enter your address:',
4923    width: 300,
4924    buttons: Roo.MessageBox.OKCANCEL,
4925    multiline: true,
4926    fn: saveAddress,
4927    animEl: 'addAddressBtn'
4928 });
4929 </code></pre>
4930          * @param {Object} config Configuration options
4931          * @return {Roo.MessageBox} This message box
4932          */
4933         show : function(options)
4934         {
4935             
4936             // this causes nightmares if you show one dialog after another
4937             // especially on callbacks..
4938              
4939             if(this.isVisible()){
4940                 
4941                 this.hide();
4942                 Roo.log("[Roo.Messagebox] Show called while message displayed:" );
4943                 Roo.log("Old Dialog Message:" +  msgEl.innerHTML );
4944                 Roo.log("New Dialog Message:" +  options.msg )
4945                 //this.alert("ERROR", "Multiple dialogs where displayed at the same time");
4946                 //throw "Roo.MessageBox ERROR : Multiple dialogs where displayed at the same time";
4947                 
4948             }
4949             var d = this.getDialog();
4950             opt = options;
4951             d.setTitle(opt.title || "&#160;");
4952             d.closeEl.setDisplayed(opt.closable !== false);
4953             activeTextEl = textboxEl;
4954             opt.prompt = opt.prompt || (opt.multiline ? true : false);
4955             if(opt.prompt){
4956                 if(opt.multiline){
4957                     textboxEl.hide();
4958                     textareaEl.show();
4959                     textareaEl.setHeight(typeof opt.multiline == "number" ?
4960                         opt.multiline : this.defaultTextHeight);
4961                     activeTextEl = textareaEl;
4962                 }else{
4963                     textboxEl.show();
4964                     textareaEl.hide();
4965                 }
4966             }else{
4967                 textboxEl.hide();
4968                 textareaEl.hide();
4969             }
4970             progressEl.setDisplayed(opt.progress === true);
4971             if (opt.progress) {
4972                 d.animate = false; // do not animate progress, as it may not have finished animating before we close it..
4973             }
4974             this.updateProgress(0);
4975             activeTextEl.dom.value = opt.value || "";
4976             if(opt.prompt){
4977                 dlg.setDefaultButton(activeTextEl);
4978             }else{
4979                 var bs = opt.buttons;
4980                 var db = null;
4981                 if(bs && bs.ok){
4982                     db = buttons["ok"];
4983                 }else if(bs && bs.yes){
4984                     db = buttons["yes"];
4985                 }
4986                 dlg.setDefaultButton(db);
4987             }
4988             bwidth = updateButtons(opt.buttons);
4989             this.updateText(opt.msg);
4990             if(opt.cls){
4991                 d.el.addClass(opt.cls);
4992             }
4993             d.proxyDrag = opt.proxyDrag === true;
4994             d.modal = opt.modal !== false;
4995             d.mask = opt.modal !== false ? mask : false;
4996             if(!d.isVisible()){
4997                 // force it to the end of the z-index stack so it gets a cursor in FF
4998                 document.body.appendChild(dlg.el.dom);
4999                 d.animateTarget = null;
5000                 d.show(options.animEl);
5001             }
5002             return this;
5003         },
5004
5005         /**
5006          * Displays a message box with a progress bar.  This message box has no buttons and is not closeable by
5007          * the user.  You are responsible for updating the progress bar as needed via {@link Roo.MessageBox#updateProgress}
5008          * and closing the message box when the process is complete.
5009          * @param {String} title The title bar text
5010          * @param {String} msg The message box body text
5011          * @return {Roo.MessageBox} This message box
5012          */
5013         progress : function(title, msg){
5014             this.show({
5015                 title : title,
5016                 msg : msg,
5017                 buttons: false,
5018                 progress:true,
5019                 closable:false,
5020                 minWidth: this.minProgressWidth,
5021                 modal : true
5022             });
5023             return this;
5024         },
5025
5026         /**
5027          * Displays a standard read-only message box with an OK button (comparable to the basic JavaScript Window.alert).
5028          * If a callback function is passed it will be called after the user clicks the button, and the
5029          * id of the button that was clicked will be passed as the only parameter to the callback
5030          * (could also be the top-right close button).
5031          * @param {String} title The title bar text
5032          * @param {String} msg The message box body text
5033          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5034          * @param {Object} scope (optional) The scope of the callback function
5035          * @return {Roo.MessageBox} This message box
5036          */
5037         alert : function(title, msg, fn, scope)
5038         {
5039             this.show({
5040                 title : title,
5041                 msg : msg,
5042                 buttons: this.OK,
5043                 fn: fn,
5044                 closable : false,
5045                 scope : scope,
5046                 modal : true
5047             });
5048             return this;
5049         },
5050
5051         /**
5052          * Displays a message box with an infinitely auto-updating progress bar.  This can be used to block user
5053          * interaction while waiting for a long-running process to complete that does not have defined intervals.
5054          * You are responsible for closing the message box when the process is complete.
5055          * @param {String} msg The message box body text
5056          * @param {String} title (optional) The title bar text
5057          * @return {Roo.MessageBox} This message box
5058          */
5059         wait : function(msg, title){
5060             this.show({
5061                 title : title,
5062                 msg : msg,
5063                 buttons: false,
5064                 closable:false,
5065                 progress:true,
5066                 modal:true,
5067                 width:300,
5068                 wait:true
5069             });
5070             waitTimer = Roo.TaskMgr.start({
5071                 run: function(i){
5072                     Roo.MessageBox.updateProgress(((((i+20)%20)+1)*5)*.01);
5073                 },
5074                 interval: 1000
5075             });
5076             return this;
5077         },
5078
5079         /**
5080          * Displays a confirmation message box with Yes and No buttons (comparable to JavaScript's Window.confirm).
5081          * If a callback function is passed it will be called after the user clicks either button, and the id of the
5082          * button that was clicked will be passed as the only parameter to the callback (could also be the top-right close button).
5083          * @param {String} title The title bar text
5084          * @param {String} msg The message box body text
5085          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5086          * @param {Object} scope (optional) The scope of the callback function
5087          * @return {Roo.MessageBox} This message box
5088          */
5089         confirm : function(title, msg, fn, scope){
5090             this.show({
5091                 title : title,
5092                 msg : msg,
5093                 buttons: this.YESNO,
5094                 fn: fn,
5095                 scope : scope,
5096                 modal : true
5097             });
5098             return this;
5099         },
5100
5101         /**
5102          * Displays a message box with OK and Cancel buttons prompting the user to enter some text (comparable to
5103          * JavaScript's Window.prompt).  The prompt can be a single-line or multi-line textbox.  If a callback function
5104          * is passed it will be called after the user clicks either button, and the id of the button that was clicked
5105          * (could also be the top-right close button) and the text that was entered will be passed as the two
5106          * parameters to the callback.
5107          * @param {String} title The title bar text
5108          * @param {String} msg The message box body text
5109          * @param {Function} fn (optional) The callback function invoked after the message box is closed
5110          * @param {Object} scope (optional) The scope of the callback function
5111          * @param {Boolean/Number} multiline (optional) True to create a multiline textbox using the defaultTextHeight
5112          * property, or the height in pixels to create the textbox (defaults to false / single-line)
5113          * @return {Roo.MessageBox} This message box
5114          */
5115         prompt : function(title, msg, fn, scope, multiline){
5116             this.show({
5117                 title : title,
5118                 msg : msg,
5119                 buttons: this.OKCANCEL,
5120                 fn: fn,
5121                 minWidth:250,
5122                 scope : scope,
5123                 prompt:true,
5124                 multiline: multiline,
5125                 modal : true
5126             });
5127             return this;
5128         },
5129
5130         /**
5131          * Button config that displays a single OK button
5132          * @type Object
5133          */
5134         OK : {ok:true},
5135         /**
5136          * Button config that displays Yes and No buttons
5137          * @type Object
5138          */
5139         YESNO : {yes:true, no:true},
5140         /**
5141          * Button config that displays OK and Cancel buttons
5142          * @type Object
5143          */
5144         OKCANCEL : {ok:true, cancel:true},
5145         /**
5146          * Button config that displays Yes, No and Cancel buttons
5147          * @type Object
5148          */
5149         YESNOCANCEL : {yes:true, no:true, cancel:true},
5150
5151         /**
5152          * The default height in pixels of the message box's multiline textarea if displayed (defaults to 75)
5153          * @type Number
5154          */
5155         defaultTextHeight : 75,
5156         /**
5157          * The maximum width in pixels of the message box (defaults to 600)
5158          * @type Number
5159          */
5160         maxWidth : 600,
5161         /**
5162          * The minimum width in pixels of the message box (defaults to 100)
5163          * @type Number
5164          */
5165         minWidth : 100,
5166         /**
5167          * The minimum width in pixels of the message box if it is a progress-style dialog.  This is useful
5168          * for setting a different minimum width than text-only dialogs may need (defaults to 250)
5169          * @type Number
5170          */
5171         minProgressWidth : 250,
5172         /**
5173          * An object containing the default button text strings that can be overriden for localized language support.
5174          * Supported properties are: ok, cancel, yes and no.
5175          * Customize the default text like so: Roo.MessageBox.buttonText.yes = "S?";
5176          * @type Object
5177          */
5178         buttonText : {
5179             ok : "OK",
5180             cancel : "Cancel",
5181             yes : "Yes",
5182             no : "No"
5183         }
5184     };
5185 }();
5186
5187 /**
5188  * Shorthand for {@link Roo.MessageBox}
5189  */
5190 Roo.MessageBox = Roo.MessageBox || Roo.bootstrap.MessageBox;
5191 Roo.Msg = Roo.Msg || Roo.MessageBox;
5192 /*
5193  * - LGPL
5194  *
5195  * navbar
5196  * 
5197  */
5198
5199 /**
5200  * @class Roo.bootstrap.Navbar
5201  * @extends Roo.bootstrap.Component
5202  * Bootstrap Navbar class
5203
5204  * @constructor
5205  * Create a new Navbar
5206  * @param {Object} config The config object
5207  */
5208
5209
5210 Roo.bootstrap.Navbar = function(config){
5211     Roo.bootstrap.Navbar.superclass.constructor.call(this, config);
5212     this.addEvents({
5213         // raw events
5214         /**
5215          * @event beforetoggle
5216          * Fire before toggle the menu
5217          * @param {Roo.EventObject} e
5218          */
5219         "beforetoggle" : true
5220     });
5221 };
5222
5223 Roo.extend(Roo.bootstrap.Navbar, Roo.bootstrap.Component,  {
5224     
5225     
5226    
5227     // private
5228     navItems : false,
5229     loadMask : false,
5230     
5231     
5232     getAutoCreate : function(){
5233         
5234         
5235         throw { message : "nav bar is now a abstract base class - use NavSimplebar / NavHeaderbar / NavSidebar etc..."};
5236         
5237     },
5238     
5239     initEvents :function ()
5240     {
5241         //Roo.log(this.el.select('.navbar-toggle',true));
5242         this.el.select('.navbar-toggle',true).on('click', this.onToggle , this);
5243         
5244         var mark = {
5245             tag: "div",
5246             cls:"x-dlg-mask"
5247         };
5248         
5249         this.maskEl = Roo.DomHelper.append(this.el, mark, true);
5250         
5251         var size = this.el.getSize();
5252         this.maskEl.setSize(size.width, size.height);
5253         this.maskEl.enableDisplayMode("block");
5254         this.maskEl.hide();
5255         
5256         if(this.loadMask){
5257             this.maskEl.show();
5258         }
5259     },
5260     
5261     
5262     getChildContainer : function()
5263     {
5264         if (this.el && this.el.select('.collapse').getCount()) {
5265             return this.el.select('.collapse',true).first();
5266         }
5267         
5268         return this.el;
5269     },
5270     
5271     mask : function()
5272     {
5273         this.maskEl.show();
5274     },
5275     
5276     unmask : function()
5277     {
5278         this.maskEl.hide();
5279     },
5280     onToggle : function()
5281     {
5282         
5283         if(this.fireEvent('beforetoggle', this) === false){
5284             return;
5285         }
5286         var ce = this.el.select('.navbar-collapse',true).first();
5287       
5288         if (!ce.hasClass('show')) {
5289            this.expand();
5290         } else {
5291             this.collapse();
5292         }
5293         
5294         
5295     
5296     },
5297     /**
5298      * Expand the navbar pulldown 
5299      */
5300     expand : function ()
5301     {
5302        
5303         var ce = this.el.select('.navbar-collapse',true).first();
5304         if (ce.hasClass('collapsing')) {
5305             return;
5306         }
5307         ce.dom.style.height = '';
5308                // show it...
5309         ce.addClass('in'); // old...
5310         ce.removeClass('collapse');
5311         ce.addClass('show');
5312         var h = ce.getHeight();
5313         Roo.log(h);
5314         ce.removeClass('show');
5315         // at this point we should be able to see it..
5316         ce.addClass('collapsing');
5317         
5318         ce.setHeight(0); // resize it ...
5319         ce.on('transitionend', function() {
5320             //Roo.log('done transition');
5321             ce.removeClass('collapsing');
5322             ce.addClass('show');
5323             ce.removeClass('collapse');
5324
5325             ce.dom.style.height = '';
5326         }, this, { single: true} );
5327         ce.setHeight(h);
5328         ce.dom.scrollTop = 0;
5329     },
5330     /**
5331      * Collapse the navbar pulldown 
5332      */
5333     collapse : function()
5334     {
5335          var ce = this.el.select('.navbar-collapse',true).first();
5336        
5337         if (ce.hasClass('collapsing') || ce.hasClass('collapse') ) {
5338             // it's collapsed or collapsing..
5339             return;
5340         }
5341         ce.removeClass('in'); // old...
5342         ce.setHeight(ce.getHeight());
5343         ce.removeClass('show');
5344         ce.addClass('collapsing');
5345         
5346         ce.on('transitionend', function() {
5347             ce.dom.style.height = '';
5348             ce.removeClass('collapsing');
5349             ce.addClass('collapse');
5350         }, this, { single: true} );
5351         ce.setHeight(0);
5352     }
5353     
5354     
5355     
5356 });
5357
5358
5359
5360  
5361
5362  /*
5363  * - LGPL
5364  *
5365  * navbar
5366  * 
5367  */
5368
5369 /**
5370  * @class Roo.bootstrap.NavSimplebar
5371  * @extends Roo.bootstrap.Navbar
5372  * Bootstrap Sidebar class
5373  *
5374  * @cfg {Boolean} inverse is inverted color
5375  * 
5376  * @cfg {String} type (nav | pills | tabs)
5377  * @cfg {Boolean} arrangement stacked | justified
5378  * @cfg {String} align (left | right) alignment
5379  * 
5380  * @cfg {Boolean} main (true|false) main nav bar? default false
5381  * @cfg {Boolean} loadMask (true|false) loadMask on the bar
5382  * 
5383  * @cfg {String} tag (header|footer|nav|div) default is nav 
5384
5385  * @cfg {String} weight (light|primary|secondary|success|danger|warning|info|dark|white) default is light.
5386  * 
5387  * 
5388  * @constructor
5389  * Create a new Sidebar
5390  * @param {Object} config The config object
5391  */
5392
5393
5394 Roo.bootstrap.NavSimplebar = function(config){
5395     Roo.bootstrap.NavSimplebar.superclass.constructor.call(this, config);
5396 };
5397
5398 Roo.extend(Roo.bootstrap.NavSimplebar, Roo.bootstrap.Navbar,  {
5399     
5400     inverse: false,
5401     
5402     type: false,
5403     arrangement: '',
5404     align : false,
5405     
5406     weight : 'light',
5407     
5408     main : false,
5409     
5410     
5411     tag : false,
5412     
5413     
5414     getAutoCreate : function(){
5415         
5416         
5417         var cfg = {
5418             tag : this.tag || 'div',
5419             cls : 'navbar roo-navbar-simple' //navbar-expand-lg ??
5420         };
5421         if (['light','white'].indexOf(this.weight) > -1) {
5422             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5423         }
5424         cfg.cls += ' bg-' + this.weight;
5425         
5426         if (this.inverse) {
5427             cfg.cls += ' navbar-inverse';
5428             
5429         }
5430         
5431         // i'm not actually sure these are really used - normally we add a navGroup to a navbar
5432         
5433         if (Roo.bootstrap.version == 4 && this.xtype == 'NavSimplebar') {
5434             return cfg;
5435         }
5436         
5437         
5438     
5439         
5440         cfg.cn = [
5441             {
5442                 cls: 'nav nav-' + this.xtype,
5443                 tag : 'ul'
5444             }
5445         ];
5446         
5447          
5448         this.type = this.type || 'nav';
5449         if (['tabs','pills'].indexOf(this.type) != -1) {
5450             cfg.cn[0].cls += ' nav-' + this.type
5451         
5452         
5453         } else {
5454             if (this.type!=='nav') {
5455                 Roo.log('nav type must be nav/tabs/pills')
5456             }
5457             cfg.cn[0].cls += ' navbar-nav'
5458         }
5459         
5460         
5461         
5462         
5463         if (['stacked','justified'].indexOf(this.arrangement) != -1) {
5464             cfg.cn[0].cls += ' nav-' + this.arrangement;
5465         }
5466         
5467         
5468         if (this.align === 'right') {
5469             cfg.cn[0].cls += ' navbar-right';
5470         }
5471         
5472         
5473         
5474         
5475         return cfg;
5476     
5477         
5478     }
5479     
5480     
5481     
5482 });
5483
5484
5485
5486  
5487
5488  
5489        /*
5490  * - LGPL
5491  *
5492  * navbar
5493  * navbar-fixed-top
5494  * navbar-expand-md  fixed-top 
5495  */
5496
5497 /**
5498  * @class Roo.bootstrap.NavHeaderbar
5499  * @extends Roo.bootstrap.NavSimplebar
5500  * Bootstrap Sidebar class
5501  *
5502  * @cfg {String} brand what is brand
5503  * @cfg {String} position (fixed-top|fixed-bottom|static-top) position
5504  * @cfg {String} brand_href href of the brand
5505  * @cfg {Boolean} srButton generate the (screen reader / mobile) sr-only button   default true
5506  * @cfg {Boolean} autohide a top nav bar header that hides on scroll.
5507  * @cfg {Boolean} desktopCenter should the header be centered on desktop using a container class
5508  * @cfg {Roo.bootstrap.Row} mobilerow - a row to display on mobile only..
5509  * 
5510  * @constructor
5511  * Create a new Sidebar
5512  * @param {Object} config The config object
5513  */
5514
5515
5516 Roo.bootstrap.NavHeaderbar = function(config){
5517     Roo.bootstrap.NavHeaderbar.superclass.constructor.call(this, config);
5518       
5519 };
5520
5521 Roo.extend(Roo.bootstrap.NavHeaderbar, Roo.bootstrap.NavSimplebar,  {
5522     
5523     position: '',
5524     brand: '',
5525     brand_href: false,
5526     srButton : true,
5527     autohide : false,
5528     desktopCenter : false,
5529    
5530     
5531     getAutoCreate : function(){
5532         
5533         var   cfg = {
5534             tag: this.nav || 'nav',
5535             cls: 'navbar navbar-expand-md',
5536             role: 'navigation',
5537             cn: []
5538         };
5539         
5540         var cn = cfg.cn;
5541         if (this.desktopCenter) {
5542             cn.push({cls : 'container', cn : []});
5543             cn = cn[0].cn;
5544         }
5545         
5546         if(this.srButton){
5547             var btn = {
5548                 tag: 'button',
5549                 type: 'button',
5550                 cls: 'navbar-toggle navbar-toggler',
5551                 'data-toggle': 'collapse',
5552                 cn: [
5553                     {
5554                         tag: 'span',
5555                         cls: 'sr-only',
5556                         html: 'Toggle navigation'
5557                     },
5558                     {
5559                         tag: 'span',
5560                         cls: 'icon-bar navbar-toggler-icon'
5561                     },
5562                     {
5563                         tag: 'span',
5564                         cls: 'icon-bar'
5565                     },
5566                     {
5567                         tag: 'span',
5568                         cls: 'icon-bar'
5569                     }
5570                 ]
5571             };
5572             
5573             cn.push( Roo.bootstrap.version == 4 ? btn : {
5574                 tag: 'div',
5575                 cls: 'navbar-header',
5576                 cn: [
5577                     btn
5578                 ]
5579             });
5580         }
5581         
5582         cn.push({
5583             tag: 'div',
5584             cls: Roo.bootstrap.version == 4  ? 'nav flex-row roo-navbar-collapse collapse navbar-collapse' : 'collapse navbar-collapse roo-navbar-collapse',
5585             cn : []
5586         });
5587         
5588         cfg.cls += this.inverse ? ' navbar-inverse navbar-dark bg-dark' : ' navbar-default';
5589         
5590         if (['light','white'].indexOf(this.weight) > -1) {
5591             cfg.cls += ['light','white'].indexOf(this.weight) > -1 ? ' navbar-light' : ' navbar-dark';
5592         }
5593         cfg.cls += ' bg-' + this.weight;
5594         
5595         
5596         if (['fixed-top','fixed-bottom','static-top'].indexOf(this.position)>-1) {
5597             cfg.cls += ' navbar-' + this.position + ' ' + this.position ;
5598             
5599             // tag can override this..
5600             
5601             cfg.tag = this.tag || (this.position  == 'fixed-bottom' ? 'footer' : 'header');
5602         }
5603         
5604         if (this.brand !== '') {
5605             var cp =  Roo.bootstrap.version == 4 ? cn : cn[0].cn;
5606             cp.unshift({ // changed from push ?? BS4 needs it at the start? - does this break or exsiting?
5607                 tag: 'a',
5608                 href: this.brand_href ? this.brand_href : '#',
5609                 cls: 'navbar-brand',
5610                 cn: [
5611                 this.brand
5612                 ]
5613             });
5614         }
5615         
5616         if(this.main){
5617             cfg.cls += ' main-nav';
5618         }
5619         
5620         
5621         return cfg;
5622
5623         
5624     },
5625     getHeaderChildContainer : function()
5626     {
5627         if (this.srButton && this.el.select('.navbar-header').getCount()) {
5628             return this.el.select('.navbar-header',true).first();
5629         }
5630         
5631         return this.getChildContainer();
5632     },
5633     
5634     getChildContainer : function()
5635     {
5636          
5637         return this.el.select('.roo-navbar-collapse',true).first();
5638          
5639         
5640     },
5641     
5642     initEvents : function()
5643     {
5644         Roo.bootstrap.NavHeaderbar.superclass.initEvents.call(this);
5645         
5646         if (this.autohide) {
5647             
5648             var prevScroll = 0;
5649             var ft = this.el;
5650             
5651             Roo.get(document).on('scroll',function(e) {
5652                 var ns = Roo.get(document).getScroll().top;
5653                 var os = prevScroll;
5654                 prevScroll = ns;
5655                 
5656                 if(ns > os){
5657                     ft.removeClass('slideDown');
5658                     ft.addClass('slideUp');
5659                     return;
5660                 }
5661                 ft.removeClass('slideUp');
5662                 ft.addClass('slideDown');
5663                  
5664               
5665           },this);
5666         }
5667     }    
5668     
5669 });
5670
5671
5672
5673  
5674
5675  /*
5676  * - LGPL
5677  *
5678  * navbar
5679  * 
5680  */
5681
5682 /**
5683  * @class Roo.bootstrap.NavSidebar
5684  * @extends Roo.bootstrap.Navbar
5685  * Bootstrap Sidebar class
5686  * 
5687  * @constructor
5688  * Create a new Sidebar
5689  * @param {Object} config The config object
5690  */
5691
5692
5693 Roo.bootstrap.NavSidebar = function(config){
5694     Roo.bootstrap.NavSidebar.superclass.constructor.call(this, config);
5695 };
5696
5697 Roo.extend(Roo.bootstrap.NavSidebar, Roo.bootstrap.Navbar,  {
5698     
5699     sidebar : true, // used by Navbar Item and NavbarGroup at present...
5700     
5701     getAutoCreate : function(){
5702         
5703         
5704         return  {
5705             tag: 'div',
5706             cls: 'sidebar sidebar-nav'
5707         };
5708     
5709         
5710     }
5711     
5712     
5713     
5714 });
5715
5716
5717
5718  
5719
5720  /*
5721  * - LGPL
5722  *
5723  * nav group
5724  * 
5725  */
5726
5727 /**
5728  * @class Roo.bootstrap.NavGroup
5729  * @extends Roo.bootstrap.Component
5730  * Bootstrap NavGroup class
5731  * @cfg {String} align (left|right)
5732  * @cfg {Boolean} inverse
5733  * @cfg {String} type (nav|pills|tab) default nav
5734  * @cfg {String} navId - reference Id for navbar.
5735
5736  * 
5737  * @constructor
5738  * Create a new nav group
5739  * @param {Object} config The config object
5740  */
5741
5742 Roo.bootstrap.NavGroup = function(config){
5743     Roo.bootstrap.NavGroup.superclass.constructor.call(this, config);
5744     this.navItems = [];
5745    
5746     Roo.bootstrap.NavGroup.register(this);
5747      this.addEvents({
5748         /**
5749              * @event changed
5750              * Fires when the active item changes
5751              * @param {Roo.bootstrap.NavGroup} this
5752              * @param {Roo.bootstrap.Navbar.Item} selected The item selected
5753              * @param {Roo.bootstrap.Navbar.Item} prev The previously selected item 
5754          */
5755         'changed': true
5756      });
5757     
5758 };
5759
5760 Roo.extend(Roo.bootstrap.NavGroup, Roo.bootstrap.Component,  {
5761     
5762     align: '',
5763     inverse: false,
5764     form: false,
5765     type: 'nav',
5766     navId : '',
5767     // private
5768     
5769     navItems : false, 
5770     
5771     getAutoCreate : function()
5772     {
5773         var cfg = Roo.apply({}, Roo.bootstrap.NavGroup.superclass.getAutoCreate.call(this));
5774         
5775         cfg = {
5776             tag : 'ul',
5777             cls: 'nav' 
5778         };
5779         if (Roo.bootstrap.version == 4) {
5780             if (['tabs','pills'].indexOf(this.type) != -1) {
5781                 cfg.cls += ' nav-' + this.type; 
5782             } else {
5783                 // trying to remove so header bar can right align top?
5784                 if (this.parent() && this.parent().xtype != 'NavHeaderbar') {
5785                     // do not use on header bar... 
5786                     cfg.cls += ' navbar-nav';
5787                 }
5788             }
5789             
5790         } else {
5791             if (['tabs','pills'].indexOf(this.type) != -1) {
5792                 cfg.cls += ' nav-' + this.type
5793             } else {
5794                 if (this.type !== 'nav') {
5795                     Roo.log('nav type must be nav/tabs/pills')
5796                 }
5797                 cfg.cls += ' navbar-nav'
5798             }
5799         }
5800         
5801         if (this.parent() && this.parent().sidebar) {
5802             cfg = {
5803                 tag: 'ul',
5804                 cls: 'dashboard-menu sidebar-menu'
5805             };
5806             
5807             return cfg;
5808         }
5809         
5810         if (this.form === true) {
5811             cfg = {
5812                 tag: 'form',
5813                 cls: 'navbar-form form-inline'
5814             };
5815             //nav navbar-right ml-md-auto
5816             if (this.align === 'right') {
5817                 cfg.cls += ' navbar-right ml-md-auto';
5818             } else {
5819                 cfg.cls += ' navbar-left';
5820             }
5821         }
5822         
5823         if (this.align === 'right') {
5824             cfg.cls += ' navbar-right ml-md-auto';
5825         } else {
5826             cfg.cls += ' mr-auto';
5827         }
5828         
5829         if (this.inverse) {
5830             cfg.cls += ' navbar-inverse';
5831             
5832         }
5833         
5834         
5835         return cfg;
5836     },
5837     /**
5838     * sets the active Navigation item
5839     * @param {Roo.bootstrap.NavItem} the new current navitem
5840     */
5841     setActiveItem : function(item)
5842     {
5843         var prev = false;
5844         Roo.each(this.navItems, function(v){
5845             if (v == item) {
5846                 return ;
5847             }
5848             if (v.isActive()) {
5849                 v.setActive(false, true);
5850                 prev = v;
5851                 
5852             }
5853             
5854         });
5855
5856         item.setActive(true, true);
5857         this.fireEvent('changed', this, item, prev);
5858         
5859         
5860     },
5861     /**
5862     * gets the active Navigation item
5863     * @return {Roo.bootstrap.NavItem} the current navitem
5864     */
5865     getActive : function()
5866     {
5867         
5868         var prev = false;
5869         Roo.each(this.navItems, function(v){
5870             
5871             if (v.isActive()) {
5872                 prev = v;
5873                 
5874             }
5875             
5876         });
5877         return prev;
5878     },
5879     
5880     indexOfNav : function()
5881     {
5882         
5883         var prev = false;
5884         Roo.each(this.navItems, function(v,i){
5885             
5886             if (v.isActive()) {
5887                 prev = i;
5888                 
5889             }
5890             
5891         });
5892         return prev;
5893     },
5894     /**
5895     * adds a Navigation item
5896     * @param {Roo.bootstrap.NavItem} the navitem to add
5897     */
5898     addItem : function(cfg)
5899     {
5900         if (this.form && Roo.bootstrap.version == 4) {
5901             cfg.tag = 'div';
5902         }
5903         var cn = new Roo.bootstrap.NavItem(cfg);
5904         this.register(cn);
5905         cn.parentId = this.id;
5906         cn.onRender(this.el, null);
5907         return cn;
5908     },
5909     /**
5910     * register a Navigation item
5911     * @param {Roo.bootstrap.NavItem} the navitem to add
5912     */
5913     register : function(item)
5914     {
5915         this.navItems.push( item);
5916         item.navId = this.navId;
5917     
5918     },
5919     
5920     /**
5921     * clear all the Navigation item
5922     */
5923    
5924     clearAll : function()
5925     {
5926         this.navItems = [];
5927         this.el.dom.innerHTML = '';
5928     },
5929     
5930     getNavItem: function(tabId)
5931     {
5932         var ret = false;
5933         Roo.each(this.navItems, function(e) {
5934             if (e.tabId == tabId) {
5935                ret =  e;
5936                return false;
5937             }
5938             return true;
5939             
5940         });
5941         return ret;
5942     },
5943     
5944     setActiveNext : function()
5945     {
5946         var i = this.indexOfNav(this.getActive());
5947         if (i > this.navItems.length) {
5948             return;
5949         }
5950         this.setActiveItem(this.navItems[i+1]);
5951     },
5952     setActivePrev : function()
5953     {
5954         var i = this.indexOfNav(this.getActive());
5955         if (i  < 1) {
5956             return;
5957         }
5958         this.setActiveItem(this.navItems[i-1]);
5959     },
5960     clearWasActive : function(except) {
5961         Roo.each(this.navItems, function(e) {
5962             if (e.tabId != except.tabId && e.was_active) {
5963                e.was_active = false;
5964                return false;
5965             }
5966             return true;
5967             
5968         });
5969     },
5970     getWasActive : function ()
5971     {
5972         var r = false;
5973         Roo.each(this.navItems, function(e) {
5974             if (e.was_active) {
5975                r = e;
5976                return false;
5977             }
5978             return true;
5979             
5980         });
5981         return r;
5982     }
5983     
5984     
5985 });
5986
5987  
5988 Roo.apply(Roo.bootstrap.NavGroup, {
5989     
5990     groups: {},
5991      /**
5992     * register a Navigation Group
5993     * @param {Roo.bootstrap.NavGroup} the navgroup to add
5994     */
5995     register : function(navgrp)
5996     {
5997         this.groups[navgrp.navId] = navgrp;
5998         
5999     },
6000     /**
6001     * fetch a Navigation Group based on the navigation ID
6002     * @param {string} the navgroup to add
6003     * @returns {Roo.bootstrap.NavGroup} the navgroup 
6004     */
6005     get: function(navId) {
6006         if (typeof(this.groups[navId]) == 'undefined') {
6007             return false;
6008             //this.register(new Roo.bootstrap.NavGroup({ navId : navId }));
6009         }
6010         return this.groups[navId] ;
6011     }
6012     
6013     
6014     
6015 });
6016
6017  /*
6018  * - LGPL
6019  *
6020  * row
6021  * 
6022  */
6023
6024 /**
6025  * @class Roo.bootstrap.NavItem
6026  * @extends Roo.bootstrap.Component
6027  * Bootstrap Navbar.NavItem class
6028  * @cfg {String} href  link to
6029  * @cfg {String} button_weight (default | primary | secondary | success | info | warning | danger | link ) default none
6030
6031  * @cfg {String} html content of button
6032  * @cfg {String} badge text inside badge
6033  * @cfg {String} badgecls (bg-green|bg-red|bg-yellow)the extra classes for the badge
6034  * @cfg {String} glyphicon DEPRICATED - use fa
6035  * @cfg {String} icon DEPRICATED - use fa
6036  * @cfg {String} fa - Fontawsome icon name (can add stuff to it like fa-2x)
6037  * @cfg {Boolean} active Is item active
6038  * @cfg {Boolean} disabled Is item disabled
6039  
6040  * @cfg {Boolean} preventDefault (true | false) default false
6041  * @cfg {String} tabId the tab that this item activates.
6042  * @cfg {String} tagtype (a|span) render as a href or span?
6043  * @cfg {Boolean} animateRef (true|false) link to element default false  
6044   
6045  * @constructor
6046  * Create a new Navbar Item
6047  * @param {Object} config The config object
6048  */
6049 Roo.bootstrap.NavItem = function(config){
6050     Roo.bootstrap.NavItem.superclass.constructor.call(this, config);
6051     this.addEvents({
6052         // raw events
6053         /**
6054          * @event click
6055          * The raw click event for the entire grid.
6056          * @param {Roo.EventObject} e
6057          */
6058         "click" : true,
6059          /**
6060             * @event changed
6061             * Fires when the active item active state changes
6062             * @param {Roo.bootstrap.NavItem} this
6063             * @param {boolean} state the new state
6064              
6065          */
6066         'changed': true,
6067         /**
6068             * @event scrollto
6069             * Fires when scroll to element
6070             * @param {Roo.bootstrap.NavItem} this
6071             * @param {Object} options
6072             * @param {Roo.EventObject} e
6073              
6074          */
6075         'scrollto': true
6076     });
6077    
6078 };
6079
6080 Roo.extend(Roo.bootstrap.NavItem, Roo.bootstrap.Component,  {
6081     
6082     href: false,
6083     html: '',
6084     badge: '',
6085     icon: false,
6086     fa : false,
6087     glyphicon: false,
6088     active: false,
6089     preventDefault : false,
6090     tabId : false,
6091     tagtype : 'a',
6092     tag: 'li',
6093     disabled : false,
6094     animateRef : false,
6095     was_active : false,
6096     button_weight : '',
6097     button_outline : false,
6098     
6099     navLink: false,
6100     
6101     getAutoCreate : function(){
6102          
6103         var cfg = {
6104             tag: this.tag,
6105             cls: 'nav-item'
6106         };
6107         
6108         if (this.active) {
6109             cfg.cls = typeof(cfg.cls) == 'undefined' ? 'active' : cfg.cls + ' active';
6110         }
6111         if (this.disabled) {
6112             cfg.cls += ' disabled';
6113         }
6114         
6115         // BS4 only?
6116         if (this.button_weight.length) {
6117             cfg.tag = this.href ? 'a' : 'button';
6118             cfg.html = this.html || '';
6119             cfg.cls += ' btn btn' + (this.button_outline ? '-outline' : '') + '-' + this.button_weight;
6120             if (this.href) {
6121                 cfg.href = this.href;
6122             }
6123             if (this.fa) {
6124                 cfg.html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + this.html + '</span>';
6125             }
6126             
6127             // menu .. should add dropdown-menu class - so no need for carat..
6128             
6129             if (this.badge !== '') {
6130                  
6131                 cfg.html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6132             }
6133             return cfg;
6134         }
6135         
6136         if (this.href || this.html || this.glyphicon || this.icon || this.fa) {
6137             cfg.cn = [
6138                 {
6139                     tag: this.tagtype,
6140                     href : this.href || "#",
6141                     html: this.html || ''
6142                 }
6143             ];
6144             if (this.tagtype == 'a') {
6145                 cfg.cn[0].cls = 'nav-link';
6146             }
6147             if (this.icon) {
6148                 cfg.cn[0].html = '<i class="'+this.icon+'"></i> <span>' + cfg.cn[0].html + '</span>';
6149             }
6150             if (this.fa) {
6151                 cfg.cn[0].html = '<i class="fa fas fa-'+this.fa+'"></i> <span>' + cfg.cn[0].html + '</span>';
6152             }
6153             if(this.glyphicon) {
6154                 cfg.cn[0].html = '<span class="glyphicon glyphicon-' + this.glyphicon + '"></span> '  + cfg.cn[0].html;
6155             }
6156             
6157             if (this.menu) {
6158                 
6159                 cfg.cn[0].html += " <span class='caret'></span>";
6160              
6161             }
6162             
6163             if (this.badge !== '') {
6164                  
6165                 cfg.cn[0].html += ' <span class="badge badge-secondary">' + this.badge + '</span>';
6166             }
6167         }
6168         
6169         
6170         
6171         return cfg;
6172     },
6173     onRender : function(ct, position)
6174     {
6175        // Roo.log("Call onRender: " + this.xtype);
6176         if (Roo.bootstrap.version == 4 && ct.dom.type != 'ul') {
6177             this.tag = 'div';
6178         }
6179         
6180         var ret = Roo.bootstrap.NavItem.superclass.onRender.call(this, ct, position);
6181         this.navLink = this.el.select('.nav-link',true).first();
6182         return ret;
6183     },
6184       
6185     
6186     initEvents: function() 
6187     {
6188         if (typeof (this.menu) != 'undefined') {
6189             this.menu.parentType = this.xtype;
6190             this.menu.triggerEl = this.el;
6191             this.menu = this.addxtype(Roo.apply({}, this.menu));
6192         }
6193         
6194         this.el.select('a',true).on('click', this.onClick, this);
6195         
6196         if(this.tagtype == 'span'){
6197             this.el.select('span',true).on('click', this.onClick, this);
6198         }
6199        
6200         // at this point parent should be available..
6201         this.parent().register(this);
6202     },
6203     
6204     onClick : function(e)
6205     {
6206         if (e.getTarget('.dropdown-menu-item')) {
6207             // did you click on a menu itemm.... - then don't trigger onclick..
6208             return;
6209         }
6210         
6211         if(
6212                 this.preventDefault || 
6213                 this.href == '#' 
6214         ){
6215             Roo.log("NavItem - prevent Default?");
6216             e.preventDefault();
6217         }
6218         
6219         if (this.disabled) {
6220             return;
6221         }
6222         
6223         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6224         if (tg && tg.transition) {
6225             Roo.log("waiting for the transitionend");
6226             return;
6227         }
6228         
6229         
6230         
6231         //Roo.log("fire event clicked");
6232         if(this.fireEvent('click', this, e) === false){
6233             return;
6234         };
6235         
6236         if(this.tagtype == 'span'){
6237             return;
6238         }
6239         
6240         //Roo.log(this.href);
6241         var ael = this.el.select('a',true).first();
6242         //Roo.log(ael);
6243         
6244         if(ael && this.animateRef && this.href.indexOf('#') > -1){
6245             //Roo.log(["test:",ael.dom.href.split("#")[0], document.location.toString().split("#")[0]]);
6246             if (ael.dom.href.split("#")[0] != document.location.toString().split("#")[0]) {
6247                 return; // ignore... - it's a 'hash' to another page.
6248             }
6249             Roo.log("NavItem - prevent Default?");
6250             e.preventDefault();
6251             this.scrollToElement(e);
6252         }
6253         
6254         
6255         var p =  this.parent();
6256    
6257         if (['tabs','pills'].indexOf(p.type)!==-1) {
6258             if (typeof(p.setActiveItem) !== 'undefined') {
6259                 p.setActiveItem(this);
6260             }
6261         }
6262         
6263         // if parent is a navbarheader....- and link is probably a '#' page ref.. then remove the expanded menu.
6264         if (p.parentType == 'NavHeaderbar' && !this.menu) {
6265             // remove the collapsed menu expand...
6266             p.parent().el.select('.roo-navbar-collapse',true).removeClass('in');  
6267         }
6268     },
6269     
6270     isActive: function () {
6271         return this.active
6272     },
6273     setActive : function(state, fire, is_was_active)
6274     {
6275         if (this.active && !state && this.navId) {
6276             this.was_active = true;
6277             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6278             if (nv) {
6279                 nv.clearWasActive(this);
6280             }
6281             
6282         }
6283         this.active = state;
6284         
6285         if (!state ) {
6286             this.el.removeClass('active');
6287             this.navLink ? this.navLink.removeClass('active') : false;
6288         } else if (!this.el.hasClass('active')) {
6289             
6290             this.el.addClass('active');
6291             if (Roo.bootstrap.version == 4 && this.navLink ) {
6292                 this.navLink.addClass('active');
6293             }
6294             
6295         }
6296         if (fire) {
6297             this.fireEvent('changed', this, state);
6298         }
6299         
6300         // show a panel if it's registered and related..
6301         
6302         if (!this.navId || !this.tabId || !state || is_was_active) {
6303             return;
6304         }
6305         
6306         var tg = Roo.bootstrap.TabGroup.get(this.navId);
6307         if (!tg) {
6308             return;
6309         }
6310         var pan = tg.getPanelByName(this.tabId);
6311         if (!pan) {
6312             return;
6313         }
6314         // if we can not flip to new panel - go back to old nav highlight..
6315         if (false == tg.showPanel(pan)) {
6316             var nv = Roo.bootstrap.NavGroup.get(this.navId);
6317             if (nv) {
6318                 var onav = nv.getWasActive();
6319                 if (onav) {
6320                     onav.setActive(true, false, true);
6321                 }
6322             }
6323             
6324         }
6325         
6326         
6327         
6328     },
6329      // this should not be here...
6330     setDisabled : function(state)
6331     {
6332         this.disabled = state;
6333         if (!state ) {
6334             this.el.removeClass('disabled');
6335         } else if (!this.el.hasClass('disabled')) {
6336             this.el.addClass('disabled');
6337         }
6338         
6339     },
6340     
6341     /**
6342      * Fetch the element to display the tooltip on.
6343      * @return {Roo.Element} defaults to this.el
6344      */
6345     tooltipEl : function()
6346     {
6347         return this.el.select('' + this.tagtype + '', true).first();
6348     },
6349     
6350     scrollToElement : function(e)
6351     {
6352         var c = document.body;
6353         
6354         /*
6355          * Firefox / IE places the overflow at the html level, unless specifically styled to behave differently.
6356          */
6357         if(Roo.isFirefox || Roo.isIE || Roo.isIE11){
6358             c = document.documentElement;
6359         }
6360         
6361         var target = Roo.get(c).select('a[name=' + this.href.split('#')[1] +']', true).first();
6362         
6363         if(!target){
6364             return;
6365         }
6366
6367         var o = target.calcOffsetsTo(c);
6368         
6369         var options = {
6370             target : target,
6371             value : o[1]
6372         };
6373         
6374         this.fireEvent('scrollto', this, options, e);
6375         
6376         Roo.get(c).scrollTo('top', options.value, true);
6377         
6378         return;
6379     }
6380 });
6381  
6382
6383  /*
6384  * - LGPL
6385  *
6386  * sidebar item
6387  *
6388  *  li
6389  *    <span> icon </span>
6390  *    <span> text </span>
6391  *    <span>badge </span>
6392  */
6393
6394 /**
6395  * @class Roo.bootstrap.NavSidebarItem
6396  * @extends Roo.bootstrap.NavItem
6397  * Bootstrap Navbar.NavSidebarItem class
6398  * {String} badgeWeight (default|primary|success|info|warning|danger)the extra classes for the badge
6399  * {Boolean} open is the menu open
6400  * {Boolean} buttonView use button as the tigger el rather that a (default false)
6401  * {String} buttonWeight (default|primary|success|info|warning|danger)the extra classes for the button
6402  * {String} buttonSize (sm|md|lg)the extra classes for the button
6403  * {Boolean} showArrow show arrow next to the text (default true)
6404  * @constructor
6405  * Create a new Navbar Button
6406  * @param {Object} config The config object
6407  */
6408 Roo.bootstrap.NavSidebarItem = function(config){
6409     Roo.bootstrap.NavSidebarItem.superclass.constructor.call(this, config);
6410     this.addEvents({
6411         // raw events
6412         /**
6413          * @event click
6414          * The raw click event for the entire grid.
6415          * @param {Roo.EventObject} e
6416          */
6417         "click" : true,
6418          /**
6419             * @event changed
6420             * Fires when the active item active state changes
6421             * @param {Roo.bootstrap.NavSidebarItem} this
6422             * @param {boolean} state the new state
6423              
6424          */
6425         'changed': true
6426     });
6427    
6428 };
6429
6430 Roo.extend(Roo.bootstrap.NavSidebarItem, Roo.bootstrap.NavItem,  {
6431     
6432     badgeWeight : 'default',
6433     
6434     open: false,
6435     
6436     buttonView : false,
6437     
6438     buttonWeight : 'default',
6439     
6440     buttonSize : 'md',
6441     
6442     showArrow : true,
6443     
6444     getAutoCreate : function(){
6445         
6446         
6447         var a = {
6448                 tag: 'a',
6449                 href : this.href || '#',
6450                 cls: '',
6451                 html : '',
6452                 cn : []
6453         };
6454         
6455         if(this.buttonView){
6456             a = {
6457                 tag: 'button',
6458                 href : this.href || '#',
6459                 cls: 'btn btn-' + this.buttonWeight + ' btn-' + this.buttonSize + 'roo-button-dropdown-toggle',
6460                 html : this.html,
6461                 cn : []
6462             };
6463         }
6464         
6465         var cfg = {
6466             tag: 'li',
6467             cls: '',
6468             cn: [ a ]
6469         };
6470         
6471         if (this.active) {
6472             cfg.cls += ' active';
6473         }
6474         
6475         if (this.disabled) {
6476             cfg.cls += ' disabled';
6477         }
6478         if (this.open) {
6479             cfg.cls += ' open x-open';
6480         }
6481         // left icon..
6482         if (this.glyphicon || this.icon) {
6483             var c = this.glyphicon  ? ('glyphicon glyphicon-'+this.glyphicon)  : this.icon;
6484             a.cn.push({ tag : 'i', cls : c }) ;
6485         }
6486         
6487         if(!this.buttonView){
6488             var span = {
6489                 tag: 'span',
6490                 html : this.html || ''
6491             };
6492
6493             a.cn.push(span);
6494             
6495         }
6496         
6497         if (this.badge !== '') {
6498             a.cn.push({ tag: 'span',  cls : 'badge pull-right badge-' + this.badgeWeight, html: this.badge }); 
6499         }
6500         
6501         if (this.menu) {
6502             
6503             if(this.showArrow){
6504                 a.cn.push({ tag : 'i', cls : 'glyphicon glyphicon-chevron-down pull-right'});
6505             }
6506             
6507             a.cls += ' dropdown-toggle treeview' ;
6508         }
6509         
6510         return cfg;
6511     },
6512     
6513     initEvents : function()
6514     { 
6515         if (typeof (this.menu) != 'undefined') {
6516             this.menu.parentType = this.xtype;
6517             this.menu.triggerEl = this.el;
6518             this.menu = this.addxtype(Roo.apply({}, this.menu));
6519         }
6520         
6521         this.el.on('click', this.onClick, this);
6522         
6523         if(this.badge !== ''){
6524             this.badgeEl = this.el.select('.badge', true).first().setVisibilityMode(Roo.Element.DISPLAY);
6525         }
6526         
6527     },
6528     
6529     onClick : function(e)
6530     {
6531         if(this.disabled){
6532             e.preventDefault();
6533             return;
6534         }
6535         
6536         if(this.preventDefault){
6537             e.preventDefault();
6538         }
6539         
6540         this.fireEvent('click', this, e);
6541     },
6542     
6543     disable : function()
6544     {
6545         this.setDisabled(true);
6546     },
6547     
6548     enable : function()
6549     {
6550         this.setDisabled(false);
6551     },
6552     
6553     setDisabled : function(state)
6554     {
6555         if(this.disabled == state){
6556             return;
6557         }
6558         
6559         this.disabled = state;
6560         
6561         if (state) {
6562             this.el.addClass('disabled');
6563             return;
6564         }
6565         
6566         this.el.removeClass('disabled');
6567         
6568         return;
6569     },
6570     
6571     setActive : function(state)
6572     {
6573         if(this.active == state){
6574             return;
6575         }
6576         
6577         this.active = state;
6578         
6579         if (state) {
6580             this.el.addClass('active');
6581             return;
6582         }
6583         
6584         this.el.removeClass('active');
6585         
6586         return;
6587     },
6588     
6589     isActive: function () 
6590     {
6591         return this.active;
6592     },
6593     
6594     setBadge : function(str)
6595     {
6596         if(!this.badgeEl){
6597             return;
6598         }
6599         
6600         this.badgeEl.dom.innerHTML = str;
6601     }
6602     
6603    
6604      
6605  
6606 });
6607  
6608
6609  /*
6610  * - LGPL
6611  *
6612  *  Breadcrumb Nav
6613  * 
6614  */
6615 Roo.namespace('Roo.bootstrap.breadcrumb');
6616
6617
6618 /**
6619  * @class Roo.bootstrap.breadcrumb.Nav
6620  * @extends Roo.bootstrap.Component
6621  * Bootstrap Breadcrumb Nav Class
6622  *  
6623  * @children Roo.bootstrap.breadcrumb.Item
6624  * 
6625  * @constructor
6626  * Create a new breadcrumb.Nav
6627  * @param {Object} config The config object
6628  */
6629
6630
6631 Roo.bootstrap.breadcrumb.Nav = function(config){
6632     Roo.bootstrap.breadcrumb.Nav.superclass.constructor.call(this, config);
6633     
6634     
6635 };
6636
6637 Roo.extend(Roo.bootstrap.breadcrumb.Nav, Roo.bootstrap.Component,  {
6638     
6639     getAutoCreate : function()
6640     {
6641
6642         var cfg = {
6643             tag: 'nav',
6644             cn : [
6645                 {
6646                     tag : 'ol',
6647                     cls : 'breadcrumb'
6648                 }
6649             ]
6650             
6651         };
6652           
6653         return cfg;
6654     },
6655     
6656     initEvents: function()
6657     {
6658         this.olEl = this.el.select('ol',true).first();    
6659     },
6660     getChildContainer : function()
6661     {
6662         return this.olEl;  
6663     }
6664     
6665 });
6666
6667  /*
6668  * - LGPL
6669  *
6670  *  Breadcrumb Item
6671  * 
6672  */
6673
6674
6675 /**
6676  * @class Roo.bootstrap.breadcrumb.Nav
6677  * @extends Roo.bootstrap.Component
6678  * Bootstrap Breadcrumb Nav Class
6679  *  
6680  * @children Roo.bootstrap.breadcrumb.Component
6681  * @cfg {String} html the content of the link.
6682  * @cfg {String} href where it links to if '#' is used the link will be handled by onClick.
6683  * @cfg {Boolean} active is it active
6684
6685  * 
6686  * @constructor
6687  * Create a new breadcrumb.Nav
6688  * @param {Object} config The config object
6689  */
6690
6691 Roo.bootstrap.breadcrumb.Item = function(config){
6692     Roo.bootstrap.breadcrumb.Item.superclass.constructor.call(this, config);
6693     this.addEvents({
6694         // img events
6695         /**
6696          * @event click
6697          * The img click event for the img.
6698          * @param {Roo.EventObject} e
6699          */
6700         "click" : true
6701     });
6702     
6703 };
6704
6705 Roo.extend(Roo.bootstrap.breadcrumb.Item, Roo.bootstrap.Component,  {
6706     
6707     href: false,
6708     html : '',
6709     
6710     getAutoCreate : function()
6711     {
6712
6713         var cfg = {
6714             tag: 'li',
6715             cls : 'breadcrumb-item' + (this.active ? ' active' : '')
6716         };
6717         if (this.href !== false) {
6718             cfg.cn = [{
6719                 tag : 'a',
6720                 href : this.href,
6721                 html : this.html
6722             }];
6723         } else {
6724             cfg.html = this.html;
6725         }
6726         
6727         return cfg;
6728     },
6729     
6730     initEvents: function()
6731     {
6732         if (this.href) {
6733             this.el.select('a', true).first().on('click',this.onClick, this)
6734         }
6735         
6736     },
6737     onClick : function(e)
6738     {
6739         e.preventDefault();
6740         this.fireEvent('click',this,  e);
6741     }
6742     
6743 });
6744
6745  /*
6746  * - LGPL
6747  *
6748  * row
6749  * 
6750  */
6751
6752 /**
6753  * @class Roo.bootstrap.Row
6754  * @extends Roo.bootstrap.Component
6755  * Bootstrap Row class (contains columns...)
6756  * 
6757  * @constructor
6758  * Create a new Row
6759  * @param {Object} config The config object
6760  */
6761
6762 Roo.bootstrap.Row = function(config){
6763     Roo.bootstrap.Row.superclass.constructor.call(this, config);
6764 };
6765
6766 Roo.extend(Roo.bootstrap.Row, Roo.bootstrap.Component,  {
6767     
6768     getAutoCreate : function(){
6769        return {
6770             cls: 'row clearfix'
6771        };
6772     }
6773     
6774     
6775 });
6776
6777  
6778
6779  /*
6780  * - LGPL
6781  *
6782  * pagination
6783  * 
6784  */
6785
6786 /**
6787  * @class Roo.bootstrap.Pagination
6788  * @extends Roo.bootstrap.Component
6789  * Bootstrap Pagination class
6790  * @cfg {String} size xs | sm | md | lg
6791  * @cfg {Boolean} inverse false | true
6792  * 
6793  * @constructor
6794  * Create a new Pagination
6795  * @param {Object} config The config object
6796  */
6797
6798 Roo.bootstrap.Pagination = function(config){
6799     Roo.bootstrap.Pagination.superclass.constructor.call(this, config);
6800 };
6801
6802 Roo.extend(Roo.bootstrap.Pagination, Roo.bootstrap.Component,  {
6803     
6804     cls: false,
6805     size: false,
6806     inverse: false,
6807     
6808     getAutoCreate : function(){
6809         var cfg = {
6810             tag: 'ul',
6811                 cls: 'pagination'
6812         };
6813         if (this.inverse) {
6814             cfg.cls += ' inverse';
6815         }
6816         if (this.html) {
6817             cfg.html=this.html;
6818         }
6819         if (this.cls) {
6820             cfg.cls += " " + this.cls;
6821         }
6822         return cfg;
6823     }
6824    
6825 });
6826
6827  
6828
6829  /*
6830  * - LGPL
6831  *
6832  * Pagination item
6833  * 
6834  */
6835
6836
6837 /**
6838  * @class Roo.bootstrap.PaginationItem
6839  * @extends Roo.bootstrap.Component
6840  * Bootstrap PaginationItem class
6841  * @cfg {String} html text
6842  * @cfg {String} href the link
6843  * @cfg {Boolean} preventDefault (true | false) default true
6844  * @cfg {Boolean} active (true | false) default false
6845  * @cfg {Boolean} disabled default false
6846  * 
6847  * 
6848  * @constructor
6849  * Create a new PaginationItem
6850  * @param {Object} config The config object
6851  */
6852
6853
6854 Roo.bootstrap.PaginationItem = function(config){
6855     Roo.bootstrap.PaginationItem.superclass.constructor.call(this, config);
6856     this.addEvents({
6857         // raw events
6858         /**
6859          * @event click
6860          * The raw click event for the entire grid.
6861          * @param {Roo.EventObject} e
6862          */
6863         "click" : true
6864     });
6865 };
6866
6867 Roo.extend(Roo.bootstrap.PaginationItem, Roo.bootstrap.Component,  {
6868     
6869     href : false,
6870     html : false,
6871     preventDefault: true,
6872     active : false,
6873     cls : false,
6874     disabled: false,
6875     
6876     getAutoCreate : function(){
6877         var cfg= {
6878             tag: 'li',
6879             cn: [
6880                 {
6881                     tag : 'a',
6882                     href : this.href ? this.href : '#',
6883                     html : this.html ? this.html : ''
6884                 }
6885             ]
6886         };
6887         
6888         if(this.cls){
6889             cfg.cls = this.cls;
6890         }
6891         
6892         if(this.disabled){
6893             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' disabled' : 'disabled';
6894         }
6895         
6896         if(this.active){
6897             cfg.cls = typeof(cfg.cls) !== 'undefined' ? cfg.cls + ' active' : 'active';
6898         }
6899         
6900         return cfg;
6901     },
6902     
6903     initEvents: function() {
6904         
6905         this.el.on('click', this.onClick, this);
6906         
6907     },
6908     onClick : function(e)
6909     {
6910         Roo.log('PaginationItem on click ');
6911         if(this.preventDefault){
6912             e.preventDefault();
6913         }
6914         
6915         if(this.disabled){
6916             return;
6917         }
6918         
6919         this.fireEvent('click', this, e);
6920     }
6921    
6922 });
6923
6924  
6925
6926  /*
6927  * - LGPL
6928  *
6929  * slider
6930  * 
6931  */
6932
6933
6934 /**
6935  * @class Roo.bootstrap.Slider
6936  * @extends Roo.bootstrap.Component
6937  * Bootstrap Slider class
6938  *    
6939  * @constructor
6940  * Create a new Slider
6941  * @param {Object} config The config object
6942  */
6943
6944 Roo.bootstrap.Slider = function(config){
6945     Roo.bootstrap.Slider.superclass.constructor.call(this, config);
6946 };
6947
6948 Roo.extend(Roo.bootstrap.Slider, Roo.bootstrap.Component,  {
6949     
6950     getAutoCreate : function(){
6951         
6952         var cfg = {
6953             tag: 'div',
6954             cls: 'slider slider-sample1 vertical-handler ui-slider ui-slider-horizontal ui-widget ui-widget-content ui-corner-all',
6955             cn: [
6956                 {
6957                     tag: 'a',
6958                     cls: 'ui-slider-handle ui-state-default ui-corner-all'
6959                 }
6960             ]
6961         };
6962         
6963         return cfg;
6964     }
6965    
6966 });
6967
6968  /*
6969  * Based on:
6970  * Ext JS Library 1.1.1
6971  * Copyright(c) 2006-2007, Ext JS, LLC.
6972  *
6973  * Originally Released Under LGPL - original licence link has changed is not relivant.
6974  *
6975  * Fork - LGPL
6976  * <script type="text/javascript">
6977  */
6978  
6979
6980 /**
6981  * @class Roo.grid.ColumnModel
6982  * @extends Roo.util.Observable
6983  * This is the default implementation of a ColumnModel used by the Grid. It defines
6984  * the columns in the grid.
6985  * <br>Usage:<br>
6986  <pre><code>
6987  var colModel = new Roo.grid.ColumnModel([
6988         {header: "Ticker", width: 60, sortable: true, locked: true},
6989         {header: "Company Name", width: 150, sortable: true},
6990         {header: "Market Cap.", width: 100, sortable: true},
6991         {header: "$ Sales", width: 100, sortable: true, renderer: money},
6992         {header: "Employees", width: 100, sortable: true, resizable: false}
6993  ]);
6994  </code></pre>
6995  * <p>
6996  
6997  * The config options listed for this class are options which may appear in each
6998  * individual column definition.
6999  * <br/>RooJS Fix - column id's are not sequential but use Roo.id() - fixes bugs with layouts.
7000  * @constructor
7001  * @param {Object} config An Array of column config objects. See this class's
7002  * config objects for details.
7003 */
7004 Roo.grid.ColumnModel = function(config){
7005         /**
7006      * The config passed into the constructor
7007      */
7008     this.config = config;
7009     this.lookup = {};
7010
7011     // if no id, create one
7012     // if the column does not have a dataIndex mapping,
7013     // map it to the order it is in the config
7014     for(var i = 0, len = config.length; i < len; i++){
7015         var c = config[i];
7016         if(typeof c.dataIndex == "undefined"){
7017             c.dataIndex = i;
7018         }
7019         if(typeof c.renderer == "string"){
7020             c.renderer = Roo.util.Format[c.renderer];
7021         }
7022         if(typeof c.id == "undefined"){
7023             c.id = Roo.id();
7024         }
7025         if(c.editor && c.editor.xtype){
7026             c.editor  = Roo.factory(c.editor, Roo.grid);
7027         }
7028         if(c.editor && c.editor.isFormField){
7029             c.editor = new Roo.grid.GridEditor(c.editor);
7030         }
7031         this.lookup[c.id] = c;
7032     }
7033
7034     /**
7035      * The width of columns which have no width specified (defaults to 100)
7036      * @type Number
7037      */
7038     this.defaultWidth = 100;
7039
7040     /**
7041      * Default sortable of columns which have no sortable specified (defaults to false)
7042      * @type Boolean
7043      */
7044     this.defaultSortable = false;
7045
7046     this.addEvents({
7047         /**
7048              * @event widthchange
7049              * Fires when the width of a column changes.
7050              * @param {ColumnModel} this
7051              * @param {Number} columnIndex The column index
7052              * @param {Number} newWidth The new width
7053              */
7054             "widthchange": true,
7055         /**
7056              * @event headerchange
7057              * Fires when the text of a header changes.
7058              * @param {ColumnModel} this
7059              * @param {Number} columnIndex The column index
7060              * @param {Number} newText The new header text
7061              */
7062             "headerchange": true,
7063         /**
7064              * @event hiddenchange
7065              * Fires when a column is hidden or "unhidden".
7066              * @param {ColumnModel} this
7067              * @param {Number} columnIndex The column index
7068              * @param {Boolean} hidden true if hidden, false otherwise
7069              */
7070             "hiddenchange": true,
7071             /**
7072          * @event columnmoved
7073          * Fires when a column is moved.
7074          * @param {ColumnModel} this
7075          * @param {Number} oldIndex
7076          * @param {Number} newIndex
7077          */
7078         "columnmoved" : true,
7079         /**
7080          * @event columlockchange
7081          * Fires when a column's locked state is changed
7082          * @param {ColumnModel} this
7083          * @param {Number} colIndex
7084          * @param {Boolean} locked true if locked
7085          */
7086         "columnlockchange" : true
7087     });
7088     Roo.grid.ColumnModel.superclass.constructor.call(this);
7089 };
7090 Roo.extend(Roo.grid.ColumnModel, Roo.util.Observable, {
7091     /**
7092      * @cfg {String} header The header text to display in the Grid view.
7093      */
7094     /**
7095      * @cfg {String} dataIndex (Optional) The name of the field in the grid's {@link Roo.data.Store}'s
7096      * {@link Roo.data.Record} definition from which to draw the column's value. If not
7097      * specified, the column's index is used as an index into the Record's data Array.
7098      */
7099     /**
7100      * @cfg {Number} width (Optional) The initial width in pixels of the column. Using this
7101      * instead of {@link Roo.grid.Grid#autoSizeColumns} is more efficient.
7102      */
7103     /**
7104      * @cfg {Boolean} sortable (Optional) True if sorting is to be allowed on this column.
7105      * Defaults to the value of the {@link #defaultSortable} property.
7106      * Whether local/remote sorting is used is specified in {@link Roo.data.Store#remoteSort}.
7107      */
7108     /**
7109      * @cfg {Boolean} locked (Optional) True to lock the column in place while scrolling the Grid.  Defaults to false.
7110      */
7111     /**
7112      * @cfg {Boolean} fixed (Optional) True if the column width cannot be changed.  Defaults to false.
7113      */
7114     /**
7115      * @cfg {Boolean} resizable (Optional) False to disable column resizing. Defaults to true.
7116      */
7117     /**
7118      * @cfg {Boolean} hidden (Optional) True to hide the column. Defaults to false.
7119      */
7120     /**
7121      * @cfg {Function} renderer (Optional) A function used to generate HTML markup for a cell
7122      * given the cell's data value. See {@link #setRenderer}. If not specified, the
7123      * default renderer returns the escaped data value. If an object is returned (bootstrap only)
7124      * then it is treated as a Roo Component object instance, and it is rendered after the initial row is rendered
7125      */
7126        /**
7127      * @cfg {Roo.grid.GridEditor} editor (Optional) For grid editors - returns the grid editor 
7128      */
7129     /**
7130      * @cfg {String} align (Optional) Set the CSS text-align property of the column.  Defaults to undefined.
7131      */
7132     /**
7133      * @cfg {String} valign (Optional) Set the CSS vertical-align property of the column (eg. middle, top, bottom etc).  Defaults to undefined.
7134      */
7135     /**
7136      * @cfg {String} cursor (Optional)
7137      */
7138     /**
7139      * @cfg {String} tooltip (Optional)
7140      */
7141     /**
7142      * @cfg {Number} xs (Optional)
7143      */
7144     /**
7145      * @cfg {Number} sm (Optional)
7146      */
7147     /**
7148      * @cfg {Number} md (Optional)
7149      */
7150     /**
7151      * @cfg {Number} lg (Optional)
7152      */
7153     /**
7154      * Returns the id of the column at the specified index.
7155      * @param {Number} index The column index
7156      * @return {String} the id
7157      */
7158     getColumnId : function(index){
7159         return this.config[index].id;
7160     },
7161
7162     /**
7163      * Returns the column for a specified id.
7164      * @param {String} id The column id
7165      * @return {Object} the column
7166      */
7167     getColumnById : function(id){
7168         return this.lookup[id];
7169     },
7170
7171     
7172     /**
7173      * Returns the column for a specified dataIndex.
7174      * @param {String} dataIndex The column dataIndex
7175      * @return {Object|Boolean} the column or false if not found
7176      */
7177     getColumnByDataIndex: function(dataIndex){
7178         var index = this.findColumnIndex(dataIndex);
7179         return index > -1 ? this.config[index] : false;
7180     },
7181     
7182     /**
7183      * Returns the index for a specified column id.
7184      * @param {String} id The column id
7185      * @return {Number} the index, or -1 if not found
7186      */
7187     getIndexById : function(id){
7188         for(var i = 0, len = this.config.length; i < len; i++){
7189             if(this.config[i].id == id){
7190                 return i;
7191             }
7192         }
7193         return -1;
7194     },
7195     
7196     /**
7197      * Returns the index for a specified column dataIndex.
7198      * @param {String} dataIndex The column dataIndex
7199      * @return {Number} the index, or -1 if not found
7200      */
7201     
7202     findColumnIndex : function(dataIndex){
7203         for(var i = 0, len = this.config.length; i < len; i++){
7204             if(this.config[i].dataIndex == dataIndex){
7205                 return i;
7206             }
7207         }
7208         return -1;
7209     },
7210     
7211     
7212     moveColumn : function(oldIndex, newIndex){
7213         var c = this.config[oldIndex];
7214         this.config.splice(oldIndex, 1);
7215         this.config.splice(newIndex, 0, c);
7216         this.dataMap = null;
7217         this.fireEvent("columnmoved", this, oldIndex, newIndex);
7218     },
7219
7220     isLocked : function(colIndex){
7221         return this.config[colIndex].locked === true;
7222     },
7223
7224     setLocked : function(colIndex, value, suppressEvent){
7225         if(this.isLocked(colIndex) == value){
7226             return;
7227         }
7228         this.config[colIndex].locked = value;
7229         if(!suppressEvent){
7230             this.fireEvent("columnlockchange", this, colIndex, value);
7231         }
7232     },
7233
7234     getTotalLockedWidth : function(){
7235         var totalWidth = 0;
7236         for(var i = 0; i < this.config.length; i++){
7237             if(this.isLocked(i) && !this.isHidden(i)){
7238                 this.totalWidth += this.getColumnWidth(i);
7239             }
7240         }
7241         return totalWidth;
7242     },
7243
7244     getLockedCount : function(){
7245         for(var i = 0, len = this.config.length; i < len; i++){
7246             if(!this.isLocked(i)){
7247                 return i;
7248             }
7249         }
7250         
7251         return this.config.length;
7252     },
7253
7254     /**
7255      * Returns the number of columns.
7256      * @return {Number}
7257      */
7258     getColumnCount : function(visibleOnly){
7259         if(visibleOnly === true){
7260             var c = 0;
7261             for(var i = 0, len = this.config.length; i < len; i++){
7262                 if(!this.isHidden(i)){
7263                     c++;
7264                 }
7265             }
7266             return c;
7267         }
7268         return this.config.length;
7269     },
7270
7271     /**
7272      * Returns the column configs that return true by the passed function that is called with (columnConfig, index)
7273      * @param {Function} fn
7274      * @param {Object} scope (optional)
7275      * @return {Array} result
7276      */
7277     getColumnsBy : function(fn, scope){
7278         var r = [];
7279         for(var i = 0, len = this.config.length; i < len; i++){
7280             var c = this.config[i];
7281             if(fn.call(scope||this, c, i) === true){
7282                 r[r.length] = c;
7283             }
7284         }
7285         return r;
7286     },
7287
7288     /**
7289      * Returns true if the specified column is sortable.
7290      * @param {Number} col The column index
7291      * @return {Boolean}
7292      */
7293     isSortable : function(col){
7294         if(typeof this.config[col].sortable == "undefined"){
7295             return this.defaultSortable;
7296         }
7297         return this.config[col].sortable;
7298     },
7299
7300     /**
7301      * Returns the rendering (formatting) function defined for the column.
7302      * @param {Number} col The column index.
7303      * @return {Function} The function used to render the cell. See {@link #setRenderer}.
7304      */
7305     getRenderer : function(col){
7306         if(!this.config[col].renderer){
7307             return Roo.grid.ColumnModel.defaultRenderer;
7308         }
7309         return this.config[col].renderer;
7310     },
7311
7312     /**
7313      * Sets the rendering (formatting) function for a column.
7314      * @param {Number} col The column index
7315      * @param {Function} fn The function to use to process the cell's raw data
7316      * to return HTML markup for the grid view. The render function is called with
7317      * the following parameters:<ul>
7318      * <li>Data value.</li>
7319      * <li>Cell metadata. An object in which you may set the following attributes:<ul>
7320      * <li>css A CSS style string to apply to the table cell.</li>
7321      * <li>attr An HTML attribute definition string to apply to the data container element <i>within</i> the table cell.</li></ul>
7322      * <li>The {@link Roo.data.Record} from which the data was extracted.</li>
7323      * <li>Row index</li>
7324      * <li>Column index</li>
7325      * <li>The {@link Roo.data.Store} object from which the Record was extracted</li></ul>
7326      */
7327     setRenderer : function(col, fn){
7328         this.config[col].renderer = fn;
7329     },
7330
7331     /**
7332      * Returns the width for the specified column.
7333      * @param {Number} col The column index
7334      * @return {Number}
7335      */
7336     getColumnWidth : function(col){
7337         return this.config[col].width * 1 || this.defaultWidth;
7338     },
7339
7340     /**
7341      * Sets the width for a column.
7342      * @param {Number} col The column index
7343      * @param {Number} width The new width
7344      */
7345     setColumnWidth : function(col, width, suppressEvent){
7346         this.config[col].width = width;
7347         this.totalWidth = null;
7348         if(!suppressEvent){
7349              this.fireEvent("widthchange", this, col, width);
7350         }
7351     },
7352
7353     /**
7354      * Returns the total width of all columns.
7355      * @param {Boolean} includeHidden True to include hidden column widths
7356      * @return {Number}
7357      */
7358     getTotalWidth : function(includeHidden){
7359         if(!this.totalWidth){
7360             this.totalWidth = 0;
7361             for(var i = 0, len = this.config.length; i < len; i++){
7362                 if(includeHidden || !this.isHidden(i)){
7363                     this.totalWidth += this.getColumnWidth(i);
7364                 }
7365             }
7366         }
7367         return this.totalWidth;
7368     },
7369
7370     /**
7371      * Returns the header for the specified column.
7372      * @param {Number} col The column index
7373      * @return {String}
7374      */
7375     getColumnHeader : function(col){
7376         return this.config[col].header;
7377     },
7378
7379     /**
7380      * Sets the header for a column.
7381      * @param {Number} col The column index
7382      * @param {String} header The new header
7383      */
7384     setColumnHeader : function(col, header){
7385         this.config[col].header = header;
7386         this.fireEvent("headerchange", this, col, header);
7387     },
7388
7389     /**
7390      * Returns the tooltip for the specified column.
7391      * @param {Number} col The column index
7392      * @return {String}
7393      */
7394     getColumnTooltip : function(col){
7395             return this.config[col].tooltip;
7396     },
7397     /**
7398      * Sets the tooltip for a column.
7399      * @param {Number} col The column index
7400      * @param {String} tooltip The new tooltip
7401      */
7402     setColumnTooltip : function(col, tooltip){
7403             this.config[col].tooltip = tooltip;
7404     },
7405
7406     /**
7407      * Returns the dataIndex for the specified column.
7408      * @param {Number} col The column index
7409      * @return {Number}
7410      */
7411     getDataIndex : function(col){
7412         return this.config[col].dataIndex;
7413     },
7414
7415     /**
7416      * Sets the dataIndex for a column.
7417      * @param {Number} col The column index
7418      * @param {Number} dataIndex The new dataIndex
7419      */
7420     setDataIndex : function(col, dataIndex){
7421         this.config[col].dataIndex = dataIndex;
7422     },
7423
7424     
7425     
7426     /**
7427      * Returns true if the cell is editable.
7428      * @param {Number} colIndex The column index
7429      * @param {Number} rowIndex The row index - this is nto actually used..?
7430      * @return {Boolean}
7431      */
7432     isCellEditable : function(colIndex, rowIndex){
7433         return (this.config[colIndex].editable || (typeof this.config[colIndex].editable == "undefined" && this.config[colIndex].editor)) ? true : false;
7434     },
7435
7436     /**
7437      * Returns the editor defined for the cell/column.
7438      * return false or null to disable editing.
7439      * @param {Number} colIndex The column index
7440      * @param {Number} rowIndex The row index
7441      * @return {Object}
7442      */
7443     getCellEditor : function(colIndex, rowIndex){
7444         return this.config[colIndex].editor;
7445     },
7446
7447     /**
7448      * Sets if a column is editable.
7449      * @param {Number} col The column index
7450      * @param {Boolean} editable True if the column is editable
7451      */
7452     setEditable : function(col, editable){
7453         this.config[col].editable = editable;
7454     },
7455
7456
7457     /**
7458      * Returns true if the column is hidden.
7459      * @param {Number} colIndex The column index
7460      * @return {Boolean}
7461      */
7462     isHidden : function(colIndex){
7463         return this.config[colIndex].hidden;
7464     },
7465
7466
7467     /**
7468      * Returns true if the column width cannot be changed
7469      */
7470     isFixed : function(colIndex){
7471         return this.config[colIndex].fixed;
7472     },
7473
7474     /**
7475      * Returns true if the column can be resized
7476      * @return {Boolean}
7477      */
7478     isResizable : function(colIndex){
7479         return colIndex >= 0 && this.config[colIndex].resizable !== false && this.config[colIndex].fixed !== true;
7480     },
7481     /**
7482      * Sets if a column is hidden.
7483      * @param {Number} colIndex The column index
7484      * @param {Boolean} hidden True if the column is hidden
7485      */
7486     setHidden : function(colIndex, hidden){
7487         this.config[colIndex].hidden = hidden;
7488         this.totalWidth = null;
7489         this.fireEvent("hiddenchange", this, colIndex, hidden);
7490     },
7491
7492     /**
7493      * Sets the editor for a column.
7494      * @param {Number} col The column index
7495      * @param {Object} editor The editor object
7496      */
7497     setEditor : function(col, editor){
7498         this.config[col].editor = editor;
7499     }
7500 });
7501
7502 Roo.grid.ColumnModel.defaultRenderer = function(value)
7503 {
7504     if(typeof value == "object") {
7505         return value;
7506     }
7507         if(typeof value == "string" && value.length < 1){
7508             return "&#160;";
7509         }
7510     
7511         return String.format("{0}", value);
7512 };
7513
7514 // Alias for backwards compatibility
7515 Roo.grid.DefaultColumnModel = Roo.grid.ColumnModel;
7516 /*
7517  * Based on:
7518  * Ext JS Library 1.1.1
7519  * Copyright(c) 2006-2007, Ext JS, LLC.
7520  *
7521  * Originally Released Under LGPL - original licence link has changed is not relivant.
7522  *
7523  * Fork - LGPL
7524  * <script type="text/javascript">
7525  */
7526  
7527 /**
7528  * @class Roo.LoadMask
7529  * A simple utility class for generically masking elements while loading data.  If the element being masked has
7530  * an underlying {@link Roo.data.Store}, the masking will be automatically synchronized with the store's loading
7531  * process and the mask element will be cached for reuse.  For all other elements, this mask will replace the
7532  * element's UpdateManager load indicator and will be destroyed after the initial load.
7533  * @constructor
7534  * Create a new LoadMask
7535  * @param {String/HTMLElement/Roo.Element} el The element or DOM node, or its id
7536  * @param {Object} config The config object
7537  */
7538 Roo.LoadMask = function(el, config){
7539     this.el = Roo.get(el);
7540     Roo.apply(this, config);
7541     if(this.store){
7542         this.store.on('beforeload', this.onBeforeLoad, this);
7543         this.store.on('load', this.onLoad, this);
7544         this.store.on('loadexception', this.onLoadException, this);
7545         this.removeMask = false;
7546     }else{
7547         var um = this.el.getUpdateManager();
7548         um.showLoadIndicator = false; // disable the default indicator
7549         um.on('beforeupdate', this.onBeforeLoad, this);
7550         um.on('update', this.onLoad, this);
7551         um.on('failure', this.onLoad, this);
7552         this.removeMask = true;
7553     }
7554 };
7555
7556 Roo.LoadMask.prototype = {
7557     /**
7558      * @cfg {Boolean} removeMask
7559      * True to create a single-use mask that is automatically destroyed after loading (useful for page loads),
7560      * False to persist the mask element reference for multiple uses (e.g., for paged data widgets).  Defaults to false.
7561      */
7562     /**
7563      * @cfg {String} msg
7564      * The text to display in a centered loading message box (defaults to 'Loading...')
7565      */
7566     msg : 'Loading...',
7567     /**
7568      * @cfg {String} msgCls
7569      * The CSS class to apply to the loading message element (defaults to "x-mask-loading")
7570      */
7571     msgCls : 'x-mask-loading',
7572
7573     /**
7574      * Read-only. True if the mask is currently disabled so that it will not be displayed (defaults to false)
7575      * @type Boolean
7576      */
7577     disabled: false,
7578
7579     /**
7580      * Disables the mask to prevent it from being displayed
7581      */
7582     disable : function(){
7583        this.disabled = true;
7584     },
7585
7586     /**
7587      * Enables the mask so that it can be displayed
7588      */
7589     enable : function(){
7590         this.disabled = false;
7591     },
7592     
7593     onLoadException : function()
7594     {
7595         Roo.log(arguments);
7596         
7597         if (typeof(arguments[3]) != 'undefined') {
7598             Roo.MessageBox.alert("Error loading",arguments[3]);
7599         } 
7600         /*
7601         try {
7602             if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
7603                 Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
7604             }   
7605         } catch(e) {
7606             
7607         }
7608         */
7609     
7610         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7611     },
7612     // private
7613     onLoad : function()
7614     {
7615         (function() { this.el.unmask(this.removeMask); }).defer(50, this);
7616     },
7617
7618     // private
7619     onBeforeLoad : function(){
7620         if(!this.disabled){
7621             (function() { this.el.mask(this.msg, this.msgCls); }).defer(50, this);
7622         }
7623     },
7624
7625     // private
7626     destroy : function(){
7627         if(this.store){
7628             this.store.un('beforeload', this.onBeforeLoad, this);
7629             this.store.un('load', this.onLoad, this);
7630             this.store.un('loadexception', this.onLoadException, this);
7631         }else{
7632             var um = this.el.getUpdateManager();
7633             um.un('beforeupdate', this.onBeforeLoad, this);
7634             um.un('update', this.onLoad, this);
7635             um.un('failure', this.onLoad, this);
7636         }
7637     }
7638 };/*
7639  * - LGPL
7640  *
7641  * table
7642  * 
7643  */
7644
7645 /**
7646  * @class Roo.bootstrap.Table
7647  * @extends Roo.bootstrap.Component
7648  * Bootstrap Table class
7649  * @cfg {String} cls table class
7650  * @cfg {String} align (left|center|right) Specifies the alignment of a table according to surrounding text
7651  * @cfg {String} bgcolor Specifies the background color for a table
7652  * @cfg {Number} border Specifies whether the table cells should have borders or not
7653  * @cfg {Number} cellpadding Specifies the space between the cell wall and the cell content
7654  * @cfg {Number} cellspacing Specifies the space between cells
7655  * @cfg {String} frame Specifies which parts of the outside borders that should be visible
7656  * @cfg {String} rules Specifies which parts of the inside borders that should be visible
7657  * @cfg {String} sortable Specifies that the table should be sortable
7658  * @cfg {String} summary Specifies a summary of the content of a table
7659  * @cfg {Number} width Specifies the width of a table
7660  * @cfg {String} layout table layout (auto | fixed | initial | inherit)
7661  * 
7662  * @cfg {boolean} striped Should the rows be alternative striped
7663  * @cfg {boolean} bordered Add borders to the table
7664  * @cfg {boolean} hover Add hover highlighting
7665  * @cfg {boolean} condensed Format condensed
7666  * @cfg {boolean} responsive Format condensed
7667  * @cfg {Boolean} loadMask (true|false) default false
7668  * @cfg {Boolean} footerShow (true|false) generate tfoot, default true
7669  * @cfg {Boolean} headerShow (true|false) generate thead, default true
7670  * @cfg {Boolean} rowSelection (true|false) default false
7671  * @cfg {Boolean} cellSelection (true|false) default false
7672  * @cfg {Boolean} scrollBody (true|false) default false - body scrolled / fixed header
7673  * @cfg {Roo.bootstrap.PagingToolbar} footer  a paging toolbar
7674  * @cfg {Boolean} lazyLoad  auto load data while scrolling to the end (default false)
7675  * @cfg {Boolean} auto_hide_footer  auto hide footer if only one page (default false)
7676  
7677  * 
7678  * @constructor
7679  * Create a new Table
7680  * @param {Object} config The config object
7681  */
7682
7683 Roo.bootstrap.Table = function(config){
7684     Roo.bootstrap.Table.superclass.constructor.call(this, config);
7685     
7686   
7687     
7688     // BC...
7689     this.rowSelection = (typeof(config.rowSelection) != 'undefined') ? config.rowSelection : this.rowSelection;
7690     this.cellSelection = (typeof(config.cellSelection) != 'undefined') ? config.cellSelection : this.cellSelection;
7691     this.headerShow = (typeof(config.thead) != 'undefined') ? config.thead : this.headerShow;
7692     this.footerShow = (typeof(config.tfoot) != 'undefined') ? config.tfoot : this.footerShow;
7693     
7694     this.sm = this.sm || {xtype: 'RowSelectionModel'};
7695     if (this.sm) {
7696         this.sm.grid = this;
7697         this.selModel = Roo.factory(this.sm, Roo.bootstrap.Table);
7698         this.sm = this.selModel;
7699         this.sm.xmodule = this.xmodule || false;
7700     }
7701     
7702     if (this.cm && typeof(this.cm.config) == 'undefined') {
7703         this.colModel = new Roo.grid.ColumnModel(this.cm);
7704         this.cm = this.colModel;
7705         this.cm.xmodule = this.xmodule || false;
7706     }
7707     if (this.store) {
7708         this.store= Roo.factory(this.store, Roo.data);
7709         this.ds = this.store;
7710         this.ds.xmodule = this.xmodule || false;
7711          
7712     }
7713     if (this.footer && this.store) {
7714         this.footer.dataSource = this.ds;
7715         this.footer = Roo.factory(this.footer);
7716     }
7717     
7718     /** @private */
7719     this.addEvents({
7720         /**
7721          * @event cellclick
7722          * Fires when a cell is clicked
7723          * @param {Roo.bootstrap.Table} this
7724          * @param {Roo.Element} el
7725          * @param {Number} rowIndex
7726          * @param {Number} columnIndex
7727          * @param {Roo.EventObject} e
7728          */
7729         "cellclick" : true,
7730         /**
7731          * @event celldblclick
7732          * Fires when a cell is double clicked
7733          * @param {Roo.bootstrap.Table} this
7734          * @param {Roo.Element} el
7735          * @param {Number} rowIndex
7736          * @param {Number} columnIndex
7737          * @param {Roo.EventObject} e
7738          */
7739         "celldblclick" : true,
7740         /**
7741          * @event rowclick
7742          * Fires when a row is clicked
7743          * @param {Roo.bootstrap.Table} this
7744          * @param {Roo.Element} el
7745          * @param {Number} rowIndex
7746          * @param {Roo.EventObject} e
7747          */
7748         "rowclick" : true,
7749         /**
7750          * @event rowdblclick
7751          * Fires when a row is double clicked
7752          * @param {Roo.bootstrap.Table} this
7753          * @param {Roo.Element} el
7754          * @param {Number} rowIndex
7755          * @param {Roo.EventObject} e
7756          */
7757         "rowdblclick" : true,
7758         /**
7759          * @event mouseover
7760          * Fires when a mouseover occur
7761          * @param {Roo.bootstrap.Table} this
7762          * @param {Roo.Element} el
7763          * @param {Number} rowIndex
7764          * @param {Number} columnIndex
7765          * @param {Roo.EventObject} e
7766          */
7767         "mouseover" : true,
7768         /**
7769          * @event mouseout
7770          * Fires when a mouseout occur
7771          * @param {Roo.bootstrap.Table} this
7772          * @param {Roo.Element} el
7773          * @param {Number} rowIndex
7774          * @param {Number} columnIndex
7775          * @param {Roo.EventObject} e
7776          */
7777         "mouseout" : true,
7778         /**
7779          * @event rowclass
7780          * Fires when a row is rendered, so you can change add a style to it.
7781          * @param {Roo.bootstrap.Table} this
7782          * @param {Object} rowcfg   contains record  rowIndex colIndex and rowClass - set rowClass to add a style.
7783          */
7784         'rowclass' : true,
7785           /**
7786          * @event rowsrendered
7787          * Fires when all the  rows have been rendered
7788          * @param {Roo.bootstrap.Table} this
7789          */
7790         'rowsrendered' : true,
7791         /**
7792          * @event contextmenu
7793          * The raw contextmenu event for the entire grid.
7794          * @param {Roo.EventObject} e
7795          */
7796         "contextmenu" : true,
7797         /**
7798          * @event rowcontextmenu
7799          * Fires when a row is right clicked
7800          * @param {Roo.bootstrap.Table} this
7801          * @param {Number} rowIndex
7802          * @param {Roo.EventObject} e
7803          */
7804         "rowcontextmenu" : true,
7805         /**
7806          * @event cellcontextmenu
7807          * Fires when a cell is right clicked
7808          * @param {Roo.bootstrap.Table} this
7809          * @param {Number} rowIndex
7810          * @param {Number} cellIndex
7811          * @param {Roo.EventObject} e
7812          */
7813          "cellcontextmenu" : true,
7814          /**
7815          * @event headercontextmenu
7816          * Fires when a header is right clicked
7817          * @param {Roo.bootstrap.Table} this
7818          * @param {Number} columnIndex
7819          * @param {Roo.EventObject} e
7820          */
7821         "headercontextmenu" : true
7822     });
7823 };
7824
7825 Roo.extend(Roo.bootstrap.Table, Roo.bootstrap.Component,  {
7826     
7827     cls: false,
7828     align: false,
7829     bgcolor: false,
7830     border: false,
7831     cellpadding: false,
7832     cellspacing: false,
7833     frame: false,
7834     rules: false,
7835     sortable: false,
7836     summary: false,
7837     width: false,
7838     striped : false,
7839     scrollBody : false,
7840     bordered: false,
7841     hover:  false,
7842     condensed : false,
7843     responsive : false,
7844     sm : false,
7845     cm : false,
7846     store : false,
7847     loadMask : false,
7848     footerShow : true,
7849     headerShow : true,
7850   
7851     rowSelection : false,
7852     cellSelection : false,
7853     layout : false,
7854     
7855     // Roo.Element - the tbody
7856     mainBody: false,
7857     // Roo.Element - thead element
7858     mainHead: false,
7859     
7860     container: false, // used by gridpanel...
7861     
7862     lazyLoad : false,
7863     
7864     CSS : Roo.util.CSS,
7865     
7866     auto_hide_footer : false,
7867     
7868     getAutoCreate : function()
7869     {
7870         var cfg = Roo.apply({}, Roo.bootstrap.Table.superclass.getAutoCreate.call(this));
7871         
7872         cfg = {
7873             tag: 'table',
7874             cls : 'table',
7875             cn : []
7876         };
7877         if (this.scrollBody) {
7878             cfg.cls += ' table-body-fixed';
7879         }    
7880         if (this.striped) {
7881             cfg.cls += ' table-striped';
7882         }
7883         
7884         if (this.hover) {
7885             cfg.cls += ' table-hover';
7886         }
7887         if (this.bordered) {
7888             cfg.cls += ' table-bordered';
7889         }
7890         if (this.condensed) {
7891             cfg.cls += ' table-condensed';
7892         }
7893         if (this.responsive) {
7894             cfg.cls += ' table-responsive';
7895         }
7896         
7897         if (this.cls) {
7898             cfg.cls+=  ' ' +this.cls;
7899         }
7900         
7901         // this lot should be simplifed...
7902         var _t = this;
7903         var cp = [
7904             'align',
7905             'bgcolor',
7906             'border',
7907             'cellpadding',
7908             'cellspacing',
7909             'frame',
7910             'rules',
7911             'sortable',
7912             'summary',
7913             'width'
7914         ].forEach(function(k) {
7915             if (_t[k]) {
7916                 cfg[k] = _t[k];
7917             }
7918         });
7919         
7920         
7921         if (this.layout) {
7922             cfg.style = (typeof(cfg.style) == 'undefined') ? ('table-layout:' + this.layout + ';') : (cfg.style + ('table-layout:' + this.layout + ';'));
7923         }
7924         
7925         if(this.store || this.cm){
7926             if(this.headerShow){
7927                 cfg.cn.push(this.renderHeader());
7928             }
7929             
7930             cfg.cn.push(this.renderBody());
7931             
7932             if(this.footerShow){
7933                 cfg.cn.push(this.renderFooter());
7934             }
7935             // where does this come from?
7936             //cfg.cls+=  ' TableGrid';
7937         }
7938         
7939         return { cn : [ cfg ] };
7940     },
7941     
7942     initEvents : function()
7943     {   
7944         if(!this.store || !this.cm){
7945             return;
7946         }
7947         if (this.selModel) {
7948             this.selModel.initEvents();
7949         }
7950         
7951         
7952         //Roo.log('initEvents with ds!!!!');
7953         
7954         this.mainBody = this.el.select('tbody', true).first();
7955         this.mainHead = this.el.select('thead', true).first();
7956         this.mainFoot = this.el.select('tfoot', true).first();
7957         
7958         
7959         
7960         var _this = this;
7961         
7962         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
7963             e.on('click', _this.sort, _this);
7964         });
7965         
7966         this.mainBody.on("click", this.onClick, this);
7967         this.mainBody.on("dblclick", this.onDblClick, this);
7968         
7969         // why is this done????? = it breaks dialogs??
7970         //this.parent().el.setStyle('position', 'relative');
7971         
7972         
7973         if (this.footer) {
7974             this.footer.parentId = this.id;
7975             this.footer.onRender(this.el.select('tfoot tr td').first(), null);
7976             
7977             if(this.lazyLoad){
7978                 this.el.select('tfoot tr td').first().addClass('hide');
7979             }
7980         } 
7981         
7982         if(this.loadMask) {
7983             this.maskEl = new Roo.LoadMask(this.el, { store : this.ds, msgCls: 'roo-el-mask-msg' });
7984         }
7985         
7986         this.store.on('load', this.onLoad, this);
7987         this.store.on('beforeload', this.onBeforeLoad, this);
7988         this.store.on('update', this.onUpdate, this);
7989         this.store.on('add', this.onAdd, this);
7990         this.store.on("clear", this.clear, this);
7991         
7992         this.el.on("contextmenu", this.onContextMenu, this);
7993         
7994         this.mainBody.on('scroll', this.onBodyScroll, this);
7995         
7996         this.cm.on("headerchange", this.onHeaderChange, this);
7997         
7998         this.cm.on("hiddenchange", this.onHiddenChange, this, arguments);
7999         
8000     },
8001     
8002     onContextMenu : function(e, t)
8003     {
8004         this.processEvent("contextmenu", e);
8005     },
8006     
8007     processEvent : function(name, e)
8008     {
8009         if (name != 'touchstart' ) {
8010             this.fireEvent(name, e);    
8011         }
8012         
8013         var t = e.getTarget();
8014         
8015         var cell = Roo.get(t);
8016         
8017         if(!cell){
8018             return;
8019         }
8020         
8021         if(cell.findParent('tfoot', false, true)){
8022             return;
8023         }
8024         
8025         if(cell.findParent('thead', false, true)){
8026             
8027             if(e.getTarget().nodeName.toLowerCase() != 'th'){
8028                 cell = Roo.get(t).findParent('th', false, true);
8029                 if (!cell) {
8030                     Roo.log("failed to find th in thead?");
8031                     Roo.log(e.getTarget());
8032                     return;
8033                 }
8034             }
8035             
8036             var cellIndex = cell.dom.cellIndex;
8037             
8038             var ename = name == 'touchstart' ? 'click' : name;
8039             this.fireEvent("header" + ename, this, cellIndex, e);
8040             
8041             return;
8042         }
8043         
8044         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8045             cell = Roo.get(t).findParent('td', false, true);
8046             if (!cell) {
8047                 Roo.log("failed to find th in tbody?");
8048                 Roo.log(e.getTarget());
8049                 return;
8050             }
8051         }
8052         
8053         var row = cell.findParent('tr', false, true);
8054         var cellIndex = cell.dom.cellIndex;
8055         var rowIndex = row.dom.rowIndex - 1;
8056         
8057         if(row !== false){
8058             
8059             this.fireEvent("row" + name, this, rowIndex, e);
8060             
8061             if(cell !== false){
8062             
8063                 this.fireEvent("cell" + name, this, rowIndex, cellIndex, e);
8064             }
8065         }
8066         
8067     },
8068     
8069     onMouseover : function(e, el)
8070     {
8071         var cell = Roo.get(el);
8072         
8073         if(!cell){
8074             return;
8075         }
8076         
8077         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8078             cell = cell.findParent('td', false, true);
8079         }
8080         
8081         var row = cell.findParent('tr', false, true);
8082         var cellIndex = cell.dom.cellIndex;
8083         var rowIndex = row.dom.rowIndex - 1; // start from 0
8084         
8085         this.fireEvent('mouseover', this, cell, rowIndex, cellIndex, e);
8086         
8087     },
8088     
8089     onMouseout : function(e, el)
8090     {
8091         var cell = Roo.get(el);
8092         
8093         if(!cell){
8094             return;
8095         }
8096         
8097         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8098             cell = cell.findParent('td', false, true);
8099         }
8100         
8101         var row = cell.findParent('tr', false, true);
8102         var cellIndex = cell.dom.cellIndex;
8103         var rowIndex = row.dom.rowIndex - 1; // start from 0
8104         
8105         this.fireEvent('mouseout', this, cell, rowIndex, cellIndex, e);
8106         
8107     },
8108     
8109     onClick : function(e, el)
8110     {
8111         var cell = Roo.get(el);
8112         
8113         if(!cell || (!this.cellSelection && !this.rowSelection)){
8114             return;
8115         }
8116         
8117         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8118             cell = cell.findParent('td', false, true);
8119         }
8120         
8121         if(!cell || typeof(cell) == 'undefined'){
8122             return;
8123         }
8124         
8125         var row = cell.findParent('tr', false, true);
8126         
8127         if(!row || typeof(row) == 'undefined'){
8128             return;
8129         }
8130         
8131         var cellIndex = cell.dom.cellIndex;
8132         var rowIndex = this.getRowIndex(row);
8133         
8134         // why??? - should these not be based on SelectionModel?
8135         if(this.cellSelection){
8136             this.fireEvent('cellclick', this, cell, rowIndex, cellIndex, e);
8137         }
8138         
8139         if(this.rowSelection){
8140             this.fireEvent('rowclick', this, row, rowIndex, e);
8141         }
8142         
8143         
8144     },
8145         
8146     onDblClick : function(e,el)
8147     {
8148         var cell = Roo.get(el);
8149         
8150         if(!cell || (!this.cellSelection && !this.rowSelection)){
8151             return;
8152         }
8153         
8154         if(e.getTarget().nodeName.toLowerCase() != 'td'){
8155             cell = cell.findParent('td', false, true);
8156         }
8157         
8158         if(!cell || typeof(cell) == 'undefined'){
8159             return;
8160         }
8161         
8162         var row = cell.findParent('tr', false, true);
8163         
8164         if(!row || typeof(row) == 'undefined'){
8165             return;
8166         }
8167         
8168         var cellIndex = cell.dom.cellIndex;
8169         var rowIndex = this.getRowIndex(row);
8170         
8171         if(this.cellSelection){
8172             this.fireEvent('celldblclick', this, cell, rowIndex, cellIndex, e);
8173         }
8174         
8175         if(this.rowSelection){
8176             this.fireEvent('rowdblclick', this, row, rowIndex, e);
8177         }
8178     },
8179     
8180     sort : function(e,el)
8181     {
8182         var col = Roo.get(el);
8183         
8184         if(!col.hasClass('sortable')){
8185             return;
8186         }
8187         
8188         var sort = col.attr('sort');
8189         var dir = 'ASC';
8190         
8191         if(col.select('i', true).first().hasClass('glyphicon-arrow-up')){
8192             dir = 'DESC';
8193         }
8194         
8195         this.store.sortInfo = {field : sort, direction : dir};
8196         
8197         if (this.footer) {
8198             Roo.log("calling footer first");
8199             this.footer.onClick('first');
8200         } else {
8201         
8202             this.store.load({ params : { start : 0 } });
8203         }
8204     },
8205     
8206     renderHeader : function()
8207     {
8208         var header = {
8209             tag: 'thead',
8210             cn : []
8211         };
8212         
8213         var cm = this.cm;
8214         this.totalWidth = 0;
8215         
8216         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8217             
8218             var config = cm.config[i];
8219             
8220             var c = {
8221                 tag: 'th',
8222                 cls : 'x-hcol-' + i,
8223                 style : '',
8224                 html: cm.getColumnHeader(i)
8225             };
8226             
8227             var hh = '';
8228             
8229             if(typeof(config.sortable) != 'undefined' && config.sortable){
8230                 c.cls = 'sortable';
8231                 c.html = '<i class="glyphicon"></i>' + c.html;
8232             }
8233             
8234             // could use BS4 hidden-..-down 
8235             
8236             if(typeof(config.lgHeader) != 'undefined'){
8237                 hh += '<span class="hidden-xs hidden-sm hidden-md ">' + config.lgHeader + '</span>';
8238             }
8239             
8240             if(typeof(config.mdHeader) != 'undefined'){
8241                 hh += '<span class="hidden-xs hidden-sm hidden-lg">' + config.mdHeader + '</span>';
8242             }
8243             
8244             if(typeof(config.smHeader) != 'undefined'){
8245                 hh += '<span class="hidden-xs hidden-md hidden-lg">' + config.smHeader + '</span>';
8246             }
8247             
8248             if(typeof(config.xsHeader) != 'undefined'){
8249                 hh += '<span class="hidden-sm hidden-md hidden-lg">' + config.xsHeader + '</span>';
8250             }
8251             
8252             if(hh.length){
8253                 c.html = hh;
8254             }
8255             
8256             if(typeof(config.tooltip) != 'undefined'){
8257                 c.tooltip = config.tooltip;
8258             }
8259             
8260             if(typeof(config.colspan) != 'undefined'){
8261                 c.colspan = config.colspan;
8262             }
8263             
8264             if(typeof(config.hidden) != 'undefined' && config.hidden){
8265                 c.style += ' display:none;';
8266             }
8267             
8268             if(typeof(config.dataIndex) != 'undefined'){
8269                 c.sort = config.dataIndex;
8270             }
8271             
8272            
8273             
8274             if(typeof(config.align) != 'undefined' && config.align.length){
8275                 c.style += ' text-align:' + config.align + ';';
8276             }
8277             
8278             if(typeof(config.width) != 'undefined'){
8279                 c.style += ' width:' + config.width + 'px;';
8280                 this.totalWidth += config.width;
8281             } else {
8282                 this.totalWidth += 100; // assume minimum of 100 per column?
8283             }
8284             
8285             if(typeof(config.cls) != 'undefined'){
8286                 c.cls = (typeof(c.cls) == 'undefined') ? config.cls : (c.cls + ' ' + config.cls);
8287             }
8288             
8289             ['xs','sm','md','lg'].map(function(size){
8290                 
8291                 if(typeof(config[size]) == 'undefined'){
8292                     return;
8293                 }
8294                  
8295                 if (!config[size]) { // 0 = hidden
8296                     // BS 4 '0' is treated as hide that column and below.
8297                     c.cls += ' hidden-' + size + ' hidden' + size + '-down';
8298                     return;
8299                 }
8300                 
8301                 c.cls += ' col-' + size + '-' + config[size] + (
8302                     size == 'xs' ? (' col-' + config[size] ) : '' // bs4 col-{num} replaces col-xs
8303                 );
8304                 
8305                 
8306             });
8307             
8308             header.cn.push(c)
8309         }
8310         
8311         return header;
8312     },
8313     
8314     renderBody : function()
8315     {
8316         var body = {
8317             tag: 'tbody',
8318             cn : [
8319                 {
8320                     tag: 'tr',
8321                     cn : [
8322                         {
8323                             tag : 'td',
8324                             colspan :  this.cm.getColumnCount()
8325                         }
8326                     ]
8327                 }
8328             ]
8329         };
8330         
8331         return body;
8332     },
8333     
8334     renderFooter : function()
8335     {
8336         var footer = {
8337             tag: 'tfoot',
8338             cn : [
8339                 {
8340                     tag: 'tr',
8341                     cn : [
8342                         {
8343                             tag : 'td',
8344                             colspan :  this.cm.getColumnCount()
8345                         }
8346                     ]
8347                 }
8348             ]
8349         };
8350         
8351         return footer;
8352     },
8353     
8354     
8355     
8356     onLoad : function()
8357     {
8358 //        Roo.log('ds onload');
8359         this.clear();
8360         
8361         var _this = this;
8362         var cm = this.cm;
8363         var ds = this.store;
8364         
8365         Roo.each(this.el.select('thead th.sortable', true).elements, function(e){
8366             e.select('i', true).removeClass(['glyphicon-arrow-up', 'glyphicon-arrow-down']);
8367             if (_this.store.sortInfo) {
8368                     
8369                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'ASC'){
8370                     e.select('i', true).addClass(['glyphicon-arrow-up']);
8371                 }
8372                 
8373                 if(e.hasClass('sortable') && e.attr('sort') == _this.store.sortInfo.field && _this.store.sortInfo.direction.toUpperCase() == 'DESC'){
8374                     e.select('i', true).addClass(['glyphicon-arrow-down']);
8375                 }
8376             }
8377         });
8378         
8379         var tbody =  this.mainBody;
8380               
8381         if(ds.getCount() > 0){
8382             ds.data.each(function(d,rowIndex){
8383                 var row =  this.renderRow(cm, ds, rowIndex);
8384                 
8385                 tbody.createChild(row);
8386                 
8387                 var _this = this;
8388                 
8389                 if(row.cellObjects.length){
8390                     Roo.each(row.cellObjects, function(r){
8391                         _this.renderCellObject(r);
8392                     })
8393                 }
8394                 
8395             }, this);
8396         }
8397         
8398         var tfoot = this.el.select('tfoot', true).first();
8399         
8400         if(this.footerShow && this.auto_hide_footer && this.mainFoot){
8401             
8402             this.mainFoot.setVisibilityMode(Roo.Element.DISPLAY).hide();
8403             
8404             var total = this.ds.getTotalCount();
8405             
8406             if(this.footer.pageSize < total){
8407                 this.mainFoot.show();
8408             }
8409         }
8410         
8411         Roo.each(this.el.select('tbody td', true).elements, function(e){
8412             e.on('mouseover', _this.onMouseover, _this);
8413         });
8414         
8415         Roo.each(this.el.select('tbody td', true).elements, function(e){
8416             e.on('mouseout', _this.onMouseout, _this);
8417         });
8418         this.fireEvent('rowsrendered', this);
8419         
8420         this.autoSize();
8421     },
8422     
8423     
8424     onUpdate : function(ds,record)
8425     {
8426         this.refreshRow(record);
8427         this.autoSize();
8428     },
8429     
8430     onRemove : function(ds, record, index, isUpdate){
8431         if(isUpdate !== true){
8432             this.fireEvent("beforerowremoved", this, index, record);
8433         }
8434         var bt = this.mainBody.dom;
8435         
8436         var rows = this.el.select('tbody > tr', true).elements;
8437         
8438         if(typeof(rows[index]) != 'undefined'){
8439             bt.removeChild(rows[index].dom);
8440         }
8441         
8442 //        if(bt.rows[index]){
8443 //            bt.removeChild(bt.rows[index]);
8444 //        }
8445         
8446         if(isUpdate !== true){
8447             //this.stripeRows(index);
8448             //this.syncRowHeights(index, index);
8449             //this.layout();
8450             this.fireEvent("rowremoved", this, index, record);
8451         }
8452     },
8453     
8454     onAdd : function(ds, records, rowIndex)
8455     {
8456         //Roo.log('on Add called');
8457         // - note this does not handle multiple adding very well..
8458         var bt = this.mainBody.dom;
8459         for (var i =0 ; i < records.length;i++) {
8460             //Roo.log('call insert row Add called on ' + rowIndex + ':' + i);
8461             //Roo.log(records[i]);
8462             //Roo.log(this.store.getAt(rowIndex+i));
8463             this.insertRow(this.store, rowIndex + i, false);
8464             return;
8465         }
8466         
8467     },
8468     
8469     
8470     refreshRow : function(record){
8471         var ds = this.store, index;
8472         if(typeof record == 'number'){
8473             index = record;
8474             record = ds.getAt(index);
8475         }else{
8476             index = ds.indexOf(record);
8477             if (index < 0) {
8478                 return; // should not happen - but seems to 
8479             }
8480         }
8481         this.insertRow(ds, index, true);
8482         this.autoSize();
8483         this.onRemove(ds, record, index+1, true);
8484         this.autoSize();
8485         //this.syncRowHeights(index, index);
8486         //this.layout();
8487         this.fireEvent("rowupdated", this, index, record);
8488     },
8489     
8490     insertRow : function(dm, rowIndex, isUpdate){
8491         
8492         if(!isUpdate){
8493             this.fireEvent("beforerowsinserted", this, rowIndex);
8494         }
8495             //var s = this.getScrollState();
8496         var row = this.renderRow(this.cm, this.store, rowIndex);
8497         // insert before rowIndex..
8498         var e = this.mainBody.createChild(row,this.getRowDom(rowIndex));
8499         
8500         var _this = this;
8501                 
8502         if(row.cellObjects.length){
8503             Roo.each(row.cellObjects, function(r){
8504                 _this.renderCellObject(r);
8505             })
8506         }
8507             
8508         if(!isUpdate){
8509             this.fireEvent("rowsinserted", this, rowIndex);
8510             //this.syncRowHeights(firstRow, lastRow);
8511             //this.stripeRows(firstRow);
8512             //this.layout();
8513         }
8514         
8515     },
8516     
8517     
8518     getRowDom : function(rowIndex)
8519     {
8520         var rows = this.el.select('tbody > tr', true).elements;
8521         
8522         return (typeof(rows[rowIndex]) == 'undefined') ? false : rows[rowIndex];
8523         
8524     },
8525     // returns the object tree for a tr..
8526   
8527     
8528     renderRow : function(cm, ds, rowIndex) 
8529     {
8530         var d = ds.getAt(rowIndex);
8531         
8532         var row = {
8533             tag : 'tr',
8534             cls : 'x-row-' + rowIndex,
8535             cn : []
8536         };
8537             
8538         var cellObjects = [];
8539         
8540         for(var i = 0, len = cm.getColumnCount(); i < len; i++){
8541             var config = cm.config[i];
8542             
8543             var renderer = cm.getRenderer(i);
8544             var value = '';
8545             var id = false;
8546             
8547             if(typeof(renderer) !== 'undefined'){
8548                 value = renderer(d.data[cm.getDataIndex(i)], false, d);
8549             }
8550             // if object are returned, then they are expected to be Roo.bootstrap.Component instances
8551             // and are rendered into the cells after the row is rendered - using the id for the element.
8552             
8553             if(typeof(value) === 'object'){
8554                 id = Roo.id();
8555                 cellObjects.push({
8556                     container : id,
8557                     cfg : value 
8558                 })
8559             }
8560             
8561             var rowcfg = {
8562                 record: d,
8563                 rowIndex : rowIndex,
8564                 colIndex : i,
8565                 rowClass : ''
8566             };
8567
8568             this.fireEvent('rowclass', this, rowcfg);
8569             
8570             var td = {
8571                 tag: 'td',
8572                 cls : rowcfg.rowClass + ' x-col-' + i,
8573                 style: '',
8574                 html: (typeof(value) === 'object') ? '' : value
8575             };
8576             
8577             if (id) {
8578                 td.id = id;
8579             }
8580             
8581             if(typeof(config.colspan) != 'undefined'){
8582                 td.colspan = config.colspan;
8583             }
8584             
8585             if(typeof(config.hidden) != 'undefined' && config.hidden){
8586                 td.style += ' display:none;';
8587             }
8588             
8589             if(typeof(config.align) != 'undefined' && config.align.length){
8590                 td.style += ' text-align:' + config.align + ';';
8591             }
8592             if(typeof(config.valign) != 'undefined' && config.valign.length){
8593                 td.style += ' vertical-align:' + config.valign + ';';
8594             }
8595             
8596             if(typeof(config.width) != 'undefined'){
8597                 td.style += ' width:' +  config.width + 'px;';
8598             }
8599             
8600             if(typeof(config.cursor) != 'undefined'){
8601                 td.style += ' cursor:' +  config.cursor + ';';
8602             }
8603             
8604             if(typeof(config.cls) != 'undefined'){
8605                 td.cls = (typeof(td.cls) == 'undefined') ? config.cls : (td.cls + ' ' + config.cls);
8606             }
8607             
8608             ['xs','sm','md','lg'].map(function(size){
8609                 
8610                 if(typeof(config[size]) == 'undefined'){
8611                     return;
8612                 }
8613                 
8614                 
8615                   
8616                 if (!config[size]) { // 0 = hidden
8617                     // BS 4 '0' is treated as hide that column and below.
8618                     td.cls += ' hidden-' + size + ' hidden' + size + '-down';
8619                     return;
8620                 }
8621                 
8622                 td.cls += ' col-' + size + '-' + config[size] + (
8623                     size == 'xs' ? (' col-' +   config[size] ) : '' // bs4 col-{num} replaces col-xs
8624                 );
8625                  
8626
8627             });
8628             
8629             row.cn.push(td);
8630            
8631         }
8632         
8633         row.cellObjects = cellObjects;
8634         
8635         return row;
8636           
8637     },
8638     
8639     
8640     
8641     onBeforeLoad : function()
8642     {
8643         
8644     },
8645      /**
8646      * Remove all rows
8647      */
8648     clear : function()
8649     {
8650         this.el.select('tbody', true).first().dom.innerHTML = '';
8651     },
8652     /**
8653      * Show or hide a row.
8654      * @param {Number} rowIndex to show or hide
8655      * @param {Boolean} state hide
8656      */
8657     setRowVisibility : function(rowIndex, state)
8658     {
8659         var bt = this.mainBody.dom;
8660         
8661         var rows = this.el.select('tbody > tr', true).elements;
8662         
8663         if(typeof(rows[rowIndex]) == 'undefined'){
8664             return;
8665         }
8666         rows[rowIndex].dom.style.display = state ? '' : 'none';
8667     },
8668     
8669     
8670     getSelectionModel : function(){
8671         if(!this.selModel){
8672             this.selModel = new Roo.bootstrap.Table.RowSelectionModel({grid: this});
8673         }
8674         return this.selModel;
8675     },
8676     /*
8677      * Render the Roo.bootstrap object from renderder
8678      */
8679     renderCellObject : function(r)
8680     {
8681         var _this = this;
8682         
8683         r.cfg.parentId = (typeof(r.container) == 'string') ? r.container : r.container.id;
8684         
8685         var t = r.cfg.render(r.container);
8686         
8687         if(r.cfg.cn){
8688             Roo.each(r.cfg.cn, function(c){
8689                 var child = {
8690                     container: t.getChildContainer(),
8691                     cfg: c
8692                 };
8693                 _this.renderCellObject(child);
8694             })
8695         }
8696     },
8697     
8698     getRowIndex : function(row)
8699     {
8700         var rowIndex = -1;
8701         
8702         Roo.each(this.el.select('tbody > tr', true).elements, function(el, index){
8703             if(el != row){
8704                 return;
8705             }
8706             
8707             rowIndex = index;
8708         });
8709         
8710         return rowIndex;
8711     },
8712      /**
8713      * Returns the grid's underlying element = used by panel.Grid
8714      * @return {Element} The element
8715      */
8716     getGridEl : function(){
8717         return this.el;
8718     },
8719      /**
8720      * Forces a resize - used by panel.Grid
8721      * @return {Element} The element
8722      */
8723     autoSize : function()
8724     {
8725         //var ctr = Roo.get(this.container.dom.parentElement);
8726         var ctr = Roo.get(this.el.dom);
8727         
8728         var thd = this.getGridEl().select('thead',true).first();
8729         var tbd = this.getGridEl().select('tbody', true).first();
8730         var tfd = this.getGridEl().select('tfoot', true).first();
8731         
8732         var cw = ctr.getWidth();
8733         this.getGridEl().select('tfoot tr, tfoot  td',true).setWidth(cw);
8734         
8735         if (tbd) {
8736             
8737             tbd.setWidth(ctr.getWidth());
8738             // if the body has a max height - and then scrolls - we should perhaps set up the height here
8739             // this needs fixing for various usage - currently only hydra job advers I think..
8740             //tdb.setHeight(
8741             //        ctr.getHeight() - ((thd ? thd.getHeight() : 0) + (tfd ? tfd.getHeight() : 0))
8742             //); 
8743             var barsize = (tbd.dom.offsetWidth - tbd.dom.clientWidth);
8744             cw -= barsize;
8745         }
8746         cw = Math.max(cw, this.totalWidth);
8747         this.getGridEl().select('tbody tr',true).setWidth(cw);
8748         
8749         // resize 'expandable coloumn?
8750         
8751         return; // we doe not have a view in this design..
8752         
8753     },
8754     onBodyScroll: function()
8755     {
8756         //Roo.log("body scrolled');" + this.mainBody.dom.scrollLeft);
8757         if(this.mainHead){
8758             this.mainHead.setStyle({
8759                 'position' : 'relative',
8760                 'left': (-1* this.mainBody.dom.scrollLeft) + 'px'
8761             });
8762         }
8763         
8764         if(this.lazyLoad){
8765             
8766             var scrollHeight = this.mainBody.dom.scrollHeight;
8767             
8768             var scrollTop = Math.ceil(this.mainBody.getScroll().top);
8769             
8770             var height = this.mainBody.getHeight();
8771             
8772             if(scrollHeight - height == scrollTop) {
8773                 
8774                 var total = this.ds.getTotalCount();
8775                 
8776                 if(this.footer.cursor + this.footer.pageSize < total){
8777                     
8778                     this.footer.ds.load({
8779                         params : {
8780                             start : this.footer.cursor + this.footer.pageSize,
8781                             limit : this.footer.pageSize
8782                         },
8783                         add : true
8784                     });
8785                 }
8786             }
8787             
8788         }
8789     },
8790     
8791     onHeaderChange : function()
8792     {
8793         var header = this.renderHeader();
8794         var table = this.el.select('table', true).first();
8795         
8796         this.mainHead.remove();
8797         this.mainHead = table.createChild(header, this.mainBody, false);
8798     },
8799     
8800     onHiddenChange : function(colModel, colIndex, hidden)
8801     {
8802         var thSelector = '#' + this.id + ' .x-hcol-' + colIndex;
8803         var tdSelector = '#' + this.id + ' .x-col-' + colIndex;
8804         
8805         this.CSS.updateRule(thSelector, "display", "");
8806         this.CSS.updateRule(tdSelector, "display", "");
8807         
8808         if(hidden){
8809             this.CSS.updateRule(thSelector, "display", "none");
8810             this.CSS.updateRule(tdSelector, "display", "none");
8811         }
8812         
8813         this.onHeaderChange();
8814         this.onLoad();
8815     },
8816     
8817     setColumnWidth: function(col_index, width)
8818     {
8819         // width = "md-2 xs-2..."
8820         if(!this.colModel.config[col_index]) {
8821             return;
8822         }
8823         
8824         var w = width.split(" ");
8825         
8826         var rows = this.el.dom.getElementsByClassName("x-col-"+col_index);
8827         
8828         var h_row = this.el.dom.getElementsByClassName("x-hcol-"+col_index);
8829         
8830         
8831         for(var j = 0; j < w.length; j++) {
8832             
8833             if(!w[j]) {
8834                 continue;
8835             }
8836             
8837             var size_cls = w[j].split("-");
8838             
8839             if(!Number.isInteger(size_cls[1] * 1)) {
8840                 continue;
8841             }
8842             
8843             if(!this.colModel.config[col_index][size_cls[0]]) {
8844                 continue;
8845             }
8846             
8847             if(!h_row[0].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8848                 continue;
8849             }
8850             
8851             h_row[0].classList.replace(
8852                 "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8853                 "col-"+size_cls[0]+"-"+size_cls[1]
8854             );
8855             
8856             for(var i = 0; i < rows.length; i++) {
8857                 
8858                 var size_cls = w[j].split("-");
8859                 
8860                 if(!Number.isInteger(size_cls[1] * 1)) {
8861                     continue;
8862                 }
8863                 
8864                 if(!this.colModel.config[col_index][size_cls[0]]) {
8865                     continue;
8866                 }
8867                 
8868                 if(!rows[i].classList.contains("col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]])) {
8869                     continue;
8870                 }
8871                 
8872                 rows[i].classList.replace(
8873                     "col-"+size_cls[0]+"-"+this.colModel.config[col_index][size_cls[0]],
8874                     "col-"+size_cls[0]+"-"+size_cls[1]
8875                 );
8876             }
8877             
8878             this.colModel.config[col_index][size_cls[0]] = size_cls[1];
8879         }
8880     }
8881 });
8882
8883  
8884
8885  /*
8886  * - LGPL
8887  *
8888  * table cell
8889  * 
8890  */
8891
8892 /**
8893  * @class Roo.bootstrap.TableCell
8894  * @extends Roo.bootstrap.Component
8895  * Bootstrap TableCell class
8896  * @cfg {String} html cell contain text
8897  * @cfg {String} cls cell class
8898  * @cfg {String} tag cell tag (td|th) default td
8899  * @cfg {String} abbr Specifies an abbreviated version of the content in a cell
8900  * @cfg {String} align Aligns the content in a cell
8901  * @cfg {String} axis Categorizes cells
8902  * @cfg {String} bgcolor Specifies the background color of a cell
8903  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
8904  * @cfg {Number} colspan Specifies the number of columns a cell should span
8905  * @cfg {String} headers Specifies one or more header cells a cell is related to
8906  * @cfg {Number} height Sets the height of a cell
8907  * @cfg {String} nowrap Specifies that the content inside a cell should not wrap
8908  * @cfg {Number} rowspan Sets the number of rows a cell should span
8909  * @cfg {String} scope Defines a way to associate header cells and data cells in a table
8910  * @cfg {String} valign Vertical aligns the content in a cell
8911  * @cfg {Number} width Specifies the width of a cell
8912  * 
8913  * @constructor
8914  * Create a new TableCell
8915  * @param {Object} config The config object
8916  */
8917
8918 Roo.bootstrap.TableCell = function(config){
8919     Roo.bootstrap.TableCell.superclass.constructor.call(this, config);
8920 };
8921
8922 Roo.extend(Roo.bootstrap.TableCell, Roo.bootstrap.Component,  {
8923     
8924     html: false,
8925     cls: false,
8926     tag: false,
8927     abbr: false,
8928     align: false,
8929     axis: false,
8930     bgcolor: false,
8931     charoff: false,
8932     colspan: false,
8933     headers: false,
8934     height: false,
8935     nowrap: false,
8936     rowspan: false,
8937     scope: false,
8938     valign: false,
8939     width: false,
8940     
8941     
8942     getAutoCreate : function(){
8943         var cfg = Roo.apply({}, Roo.bootstrap.TableCell.superclass.getAutoCreate.call(this));
8944         
8945         cfg = {
8946             tag: 'td'
8947         };
8948         
8949         if(this.tag){
8950             cfg.tag = this.tag;
8951         }
8952         
8953         if (this.html) {
8954             cfg.html=this.html
8955         }
8956         if (this.cls) {
8957             cfg.cls=this.cls
8958         }
8959         if (this.abbr) {
8960             cfg.abbr=this.abbr
8961         }
8962         if (this.align) {
8963             cfg.align=this.align
8964         }
8965         if (this.axis) {
8966             cfg.axis=this.axis
8967         }
8968         if (this.bgcolor) {
8969             cfg.bgcolor=this.bgcolor
8970         }
8971         if (this.charoff) {
8972             cfg.charoff=this.charoff
8973         }
8974         if (this.colspan) {
8975             cfg.colspan=this.colspan
8976         }
8977         if (this.headers) {
8978             cfg.headers=this.headers
8979         }
8980         if (this.height) {
8981             cfg.height=this.height
8982         }
8983         if (this.nowrap) {
8984             cfg.nowrap=this.nowrap
8985         }
8986         if (this.rowspan) {
8987             cfg.rowspan=this.rowspan
8988         }
8989         if (this.scope) {
8990             cfg.scope=this.scope
8991         }
8992         if (this.valign) {
8993             cfg.valign=this.valign
8994         }
8995         if (this.width) {
8996             cfg.width=this.width
8997         }
8998         
8999         
9000         return cfg;
9001     }
9002    
9003 });
9004
9005  
9006
9007  /*
9008  * - LGPL
9009  *
9010  * table row
9011  * 
9012  */
9013
9014 /**
9015  * @class Roo.bootstrap.TableRow
9016  * @extends Roo.bootstrap.Component
9017  * Bootstrap TableRow class
9018  * @cfg {String} cls row class
9019  * @cfg {String} align Aligns the content in a table row
9020  * @cfg {String} bgcolor Specifies a background color for a table row
9021  * @cfg {Number} charoff Sets the number of characters the content will be aligned from the character specified by the char attribute
9022  * @cfg {String} valign Vertical aligns the content in a table row
9023  * 
9024  * @constructor
9025  * Create a new TableRow
9026  * @param {Object} config The config object
9027  */
9028
9029 Roo.bootstrap.TableRow = function(config){
9030     Roo.bootstrap.TableRow.superclass.constructor.call(this, config);
9031 };
9032
9033 Roo.extend(Roo.bootstrap.TableRow, Roo.bootstrap.Component,  {
9034     
9035     cls: false,
9036     align: false,
9037     bgcolor: false,
9038     charoff: false,
9039     valign: false,
9040     
9041     getAutoCreate : function(){
9042         var cfg = Roo.apply({}, Roo.bootstrap.TableRow.superclass.getAutoCreate.call(this));
9043         
9044         cfg = {
9045             tag: 'tr'
9046         };
9047             
9048         if(this.cls){
9049             cfg.cls = this.cls;
9050         }
9051         if(this.align){
9052             cfg.align = this.align;
9053         }
9054         if(this.bgcolor){
9055             cfg.bgcolor = this.bgcolor;
9056         }
9057         if(this.charoff){
9058             cfg.charoff = this.charoff;
9059         }
9060         if(this.valign){
9061             cfg.valign = this.valign;
9062         }
9063         
9064         return cfg;
9065     }
9066    
9067 });
9068
9069  
9070
9071  /*
9072  * - LGPL
9073  *
9074  * table body
9075  * 
9076  */
9077
9078 /**
9079  * @class Roo.bootstrap.TableBody
9080  * @extends Roo.bootstrap.Component
9081  * Bootstrap TableBody class
9082  * @cfg {String} cls element class
9083  * @cfg {String} tag element tag (thead|tbody|tfoot) default tbody
9084  * @cfg {String} align Aligns the content inside the element
9085  * @cfg {Number} charoff Sets the number of characters the content inside the element will be aligned from the character specified by the char attribute
9086  * @cfg {String} valign Vertical aligns the content inside the <tbody> element
9087  * 
9088  * @constructor
9089  * Create a new TableBody
9090  * @param {Object} config The config object
9091  */
9092
9093 Roo.bootstrap.TableBody = function(config){
9094     Roo.bootstrap.TableBody.superclass.constructor.call(this, config);
9095 };
9096
9097 Roo.extend(Roo.bootstrap.TableBody, Roo.bootstrap.Component,  {
9098     
9099     cls: false,
9100     tag: false,
9101     align: false,
9102     charoff: false,
9103     valign: false,
9104     
9105     getAutoCreate : function(){
9106         var cfg = Roo.apply({}, Roo.bootstrap.TableBody.superclass.getAutoCreate.call(this));
9107         
9108         cfg = {
9109             tag: 'tbody'
9110         };
9111             
9112         if (this.cls) {
9113             cfg.cls=this.cls
9114         }
9115         if(this.tag){
9116             cfg.tag = this.tag;
9117         }
9118         
9119         if(this.align){
9120             cfg.align = this.align;
9121         }
9122         if(this.charoff){
9123             cfg.charoff = this.charoff;
9124         }
9125         if(this.valign){
9126             cfg.valign = this.valign;
9127         }
9128         
9129         return cfg;
9130     }
9131     
9132     
9133 //    initEvents : function()
9134 //    {
9135 //        
9136 //        if(!this.store){
9137 //            return;
9138 //        }
9139 //        
9140 //        this.store = Roo.factory(this.store, Roo.data);
9141 //        this.store.on('load', this.onLoad, this);
9142 //        
9143 //        this.store.load();
9144 //        
9145 //    },
9146 //    
9147 //    onLoad: function () 
9148 //    {   
9149 //        this.fireEvent('load', this);
9150 //    }
9151 //    
9152 //   
9153 });
9154
9155  
9156
9157  /*
9158  * Based on:
9159  * Ext JS Library 1.1.1
9160  * Copyright(c) 2006-2007, Ext JS, LLC.
9161  *
9162  * Originally Released Under LGPL - original licence link has changed is not relivant.
9163  *
9164  * Fork - LGPL
9165  * <script type="text/javascript">
9166  */
9167
9168 // as we use this in bootstrap.
9169 Roo.namespace('Roo.form');
9170  /**
9171  * @class Roo.form.Action
9172  * Internal Class used to handle form actions
9173  * @constructor
9174  * @param {Roo.form.BasicForm} el The form element or its id
9175  * @param {Object} config Configuration options
9176  */
9177
9178  
9179  
9180 // define the action interface
9181 Roo.form.Action = function(form, options){
9182     this.form = form;
9183     this.options = options || {};
9184 };
9185 /**
9186  * Client Validation Failed
9187  * @const 
9188  */
9189 Roo.form.Action.CLIENT_INVALID = 'client';
9190 /**
9191  * Server Validation Failed
9192  * @const 
9193  */
9194 Roo.form.Action.SERVER_INVALID = 'server';
9195  /**
9196  * Connect to Server Failed
9197  * @const 
9198  */
9199 Roo.form.Action.CONNECT_FAILURE = 'connect';
9200 /**
9201  * Reading Data from Server Failed
9202  * @const 
9203  */
9204 Roo.form.Action.LOAD_FAILURE = 'load';
9205
9206 Roo.form.Action.prototype = {
9207     type : 'default',
9208     failureType : undefined,
9209     response : undefined,
9210     result : undefined,
9211
9212     // interface method
9213     run : function(options){
9214
9215     },
9216
9217     // interface method
9218     success : function(response){
9219
9220     },
9221
9222     // interface method
9223     handleResponse : function(response){
9224
9225     },
9226
9227     // default connection failure
9228     failure : function(response){
9229         
9230         this.response = response;
9231         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9232         this.form.afterAction(this, false);
9233     },
9234
9235     processResponse : function(response){
9236         this.response = response;
9237         if(!response.responseText){
9238             return true;
9239         }
9240         this.result = this.handleResponse(response);
9241         return this.result;
9242     },
9243
9244     // utility functions used internally
9245     getUrl : function(appendParams){
9246         var url = this.options.url || this.form.url || this.form.el.dom.action;
9247         if(appendParams){
9248             var p = this.getParams();
9249             if(p){
9250                 url += (url.indexOf('?') != -1 ? '&' : '?') + p;
9251             }
9252         }
9253         return url;
9254     },
9255
9256     getMethod : function(){
9257         return (this.options.method || this.form.method || this.form.el.dom.method || 'POST').toUpperCase();
9258     },
9259
9260     getParams : function(){
9261         var bp = this.form.baseParams;
9262         var p = this.options.params;
9263         if(p){
9264             if(typeof p == "object"){
9265                 p = Roo.urlEncode(Roo.applyIf(p, bp));
9266             }else if(typeof p == 'string' && bp){
9267                 p += '&' + Roo.urlEncode(bp);
9268             }
9269         }else if(bp){
9270             p = Roo.urlEncode(bp);
9271         }
9272         return p;
9273     },
9274
9275     createCallback : function(){
9276         return {
9277             success: this.success,
9278             failure: this.failure,
9279             scope: this,
9280             timeout: (this.form.timeout*1000),
9281             upload: this.form.fileUpload ? this.success : undefined
9282         };
9283     }
9284 };
9285
9286 Roo.form.Action.Submit = function(form, options){
9287     Roo.form.Action.Submit.superclass.constructor.call(this, form, options);
9288 };
9289
9290 Roo.extend(Roo.form.Action.Submit, Roo.form.Action, {
9291     type : 'submit',
9292
9293     haveProgress : false,
9294     uploadComplete : false,
9295     
9296     // uploadProgress indicator.
9297     uploadProgress : function()
9298     {
9299         if (!this.form.progressUrl) {
9300             return;
9301         }
9302         
9303         if (!this.haveProgress) {
9304             Roo.MessageBox.progress("Uploading", "Uploading");
9305         }
9306         if (this.uploadComplete) {
9307            Roo.MessageBox.hide();
9308            return;
9309         }
9310         
9311         this.haveProgress = true;
9312    
9313         var uid = this.form.findField('UPLOAD_IDENTIFIER').getValue();
9314         
9315         var c = new Roo.data.Connection();
9316         c.request({
9317             url : this.form.progressUrl,
9318             params: {
9319                 id : uid
9320             },
9321             method: 'GET',
9322             success : function(req){
9323                //console.log(data);
9324                 var rdata = false;
9325                 var edata;
9326                 try  {
9327                    rdata = Roo.decode(req.responseText)
9328                 } catch (e) {
9329                     Roo.log("Invalid data from server..");
9330                     Roo.log(edata);
9331                     return;
9332                 }
9333                 if (!rdata || !rdata.success) {
9334                     Roo.log(rdata);
9335                     Roo.MessageBox.alert(Roo.encode(rdata));
9336                     return;
9337                 }
9338                 var data = rdata.data;
9339                 
9340                 if (this.uploadComplete) {
9341                    Roo.MessageBox.hide();
9342                    return;
9343                 }
9344                    
9345                 if (data){
9346                     Roo.MessageBox.updateProgress(data.bytes_uploaded/data.bytes_total,
9347                        Math.floor((data.bytes_total - data.bytes_uploaded)/1000) + 'k remaining'
9348                     );
9349                 }
9350                 this.uploadProgress.defer(2000,this);
9351             },
9352        
9353             failure: function(data) {
9354                 Roo.log('progress url failed ');
9355                 Roo.log(data);
9356             },
9357             scope : this
9358         });
9359            
9360     },
9361     
9362     
9363     run : function()
9364     {
9365         // run get Values on the form, so it syncs any secondary forms.
9366         this.form.getValues();
9367         
9368         var o = this.options;
9369         var method = this.getMethod();
9370         var isPost = method == 'POST';
9371         if(o.clientValidation === false || this.form.isValid()){
9372             
9373             if (this.form.progressUrl) {
9374                 this.form.findField('UPLOAD_IDENTIFIER').setValue(
9375                     (new Date() * 1) + '' + Math.random());
9376                     
9377             } 
9378             
9379             
9380             Roo.Ajax.request(Roo.apply(this.createCallback(), {
9381                 form:this.form.el.dom,
9382                 url:this.getUrl(!isPost),
9383                 method: method,
9384                 params:isPost ? this.getParams() : null,
9385                 isUpload: this.form.fileUpload,
9386                 formData : this.form.formData
9387             }));
9388             
9389             this.uploadProgress();
9390
9391         }else if (o.clientValidation !== false){ // client validation failed
9392             this.failureType = Roo.form.Action.CLIENT_INVALID;
9393             this.form.afterAction(this, false);
9394         }
9395     },
9396
9397     success : function(response)
9398     {
9399         this.uploadComplete= true;
9400         if (this.haveProgress) {
9401             Roo.MessageBox.hide();
9402         }
9403         
9404         
9405         var result = this.processResponse(response);
9406         if(result === true || result.success){
9407             this.form.afterAction(this, true);
9408             return;
9409         }
9410         if(result.errors){
9411             this.form.markInvalid(result.errors);
9412             this.failureType = Roo.form.Action.SERVER_INVALID;
9413         }
9414         this.form.afterAction(this, false);
9415     },
9416     failure : function(response)
9417     {
9418         this.uploadComplete= true;
9419         if (this.haveProgress) {
9420             Roo.MessageBox.hide();
9421         }
9422         
9423         this.response = response;
9424         this.failureType = Roo.form.Action.CONNECT_FAILURE;
9425         this.form.afterAction(this, false);
9426     },
9427     
9428     handleResponse : function(response){
9429         if(this.form.errorReader){
9430             var rs = this.form.errorReader.read(response);
9431             var errors = [];
9432             if(rs.records){
9433                 for(var i = 0, len = rs.records.length; i < len; i++) {
9434                     var r = rs.records[i];
9435                     errors[i] = r.data;
9436                 }
9437             }
9438             if(errors.length < 1){
9439                 errors = null;
9440             }
9441             return {
9442                 success : rs.success,
9443                 errors : errors
9444             };
9445         }
9446         var ret = false;
9447         try {
9448             ret = Roo.decode(response.responseText);
9449         } catch (e) {
9450             ret = {
9451                 success: false,
9452                 errorMsg: "Failed to read server message: " + (response ? response.responseText : ' - no message'),
9453                 errors : []
9454             };
9455         }
9456         return ret;
9457         
9458     }
9459 });
9460
9461
9462 Roo.form.Action.Load = function(form, options){
9463     Roo.form.Action.Load.superclass.constructor.call(this, form, options);
9464     this.reader = this.form.reader;
9465 };
9466
9467 Roo.extend(Roo.form.Action.Load, Roo.form.Action, {
9468     type : 'load',
9469
9470     run : function(){
9471         
9472         Roo.Ajax.request(Roo.apply(
9473                 this.createCallback(), {
9474                     method:this.getMethod(),
9475                     url:this.getUrl(false),
9476                     params:this.getParams()
9477         }));
9478     },
9479
9480     success : function(response){
9481         
9482         var result = this.processResponse(response);
9483         if(result === true || !result.success || !result.data){
9484             this.failureType = Roo.form.Action.LOAD_FAILURE;
9485             this.form.afterAction(this, false);
9486             return;
9487         }
9488         this.form.clearInvalid();
9489         this.form.setValues(result.data);
9490         this.form.afterAction(this, true);
9491     },
9492
9493     handleResponse : function(response){
9494         if(this.form.reader){
9495             var rs = this.form.reader.read(response);
9496             var data = rs.records && rs.records[0] ? rs.records[0].data : null;
9497             return {
9498                 success : rs.success,
9499                 data : data
9500             };
9501         }
9502         return Roo.decode(response.responseText);
9503     }
9504 });
9505
9506 Roo.form.Action.ACTION_TYPES = {
9507     'load' : Roo.form.Action.Load,
9508     'submit' : Roo.form.Action.Submit
9509 };/*
9510  * - LGPL
9511  *
9512  * form
9513  *
9514  */
9515
9516 /**
9517  * @class Roo.bootstrap.Form
9518  * @extends Roo.bootstrap.Component
9519  * Bootstrap Form class
9520  * @cfg {String} method  GET | POST (default POST)
9521  * @cfg {String} labelAlign top | left (default top)
9522  * @cfg {String} align left  | right - for navbars
9523  * @cfg {Boolean} loadMask load mask when submit (default true)
9524
9525  *
9526  * @constructor
9527  * Create a new Form
9528  * @param {Object} config The config object
9529  */
9530
9531
9532 Roo.bootstrap.Form = function(config){
9533     
9534     Roo.bootstrap.Form.superclass.constructor.call(this, config);
9535     
9536     Roo.bootstrap.Form.popover.apply();
9537     
9538     this.addEvents({
9539         /**
9540          * @event clientvalidation
9541          * If the monitorValid config option is true, this event fires repetitively to notify of valid state
9542          * @param {Form} this
9543          * @param {Boolean} valid true if the form has passed client-side validation
9544          */
9545         clientvalidation: true,
9546         /**
9547          * @event beforeaction
9548          * Fires before any action is performed. Return false to cancel the action.
9549          * @param {Form} this
9550          * @param {Action} action The action to be performed
9551          */
9552         beforeaction: true,
9553         /**
9554          * @event actionfailed
9555          * Fires when an action fails.
9556          * @param {Form} this
9557          * @param {Action} action The action that failed
9558          */
9559         actionfailed : true,
9560         /**
9561          * @event actioncomplete
9562          * Fires when an action is completed.
9563          * @param {Form} this
9564          * @param {Action} action The action that completed
9565          */
9566         actioncomplete : true
9567     });
9568 };
9569
9570 Roo.extend(Roo.bootstrap.Form, Roo.bootstrap.Component,  {
9571
9572      /**
9573      * @cfg {String} method
9574      * The request method to use (GET or POST) for form actions if one isn't supplied in the action options.
9575      */
9576     method : 'POST',
9577     /**
9578      * @cfg {String} url
9579      * The URL to use for form actions if one isn't supplied in the action options.
9580      */
9581     /**
9582      * @cfg {Boolean} fileUpload
9583      * Set to true if this form is a file upload.
9584      */
9585
9586     /**
9587      * @cfg {Object} baseParams
9588      * Parameters to pass with all requests. e.g. baseParams: {id: '123', foo: 'bar'}.
9589      */
9590
9591     /**
9592      * @cfg {Number} timeout Timeout for form actions in seconds (default is 30 seconds).
9593      */
9594     timeout: 30,
9595     /**
9596      * @cfg {Sting} align (left|right) for navbar forms
9597      */
9598     align : 'left',
9599
9600     // private
9601     activeAction : null,
9602
9603     /**
9604      * By default wait messages are displayed with Roo.MessageBox.wait. You can target a specific
9605      * element by passing it or its id or mask the form itself by passing in true.
9606      * @type Mixed
9607      */
9608     waitMsgTarget : false,
9609
9610     loadMask : true,
9611     
9612     /**
9613      * @cfg {Boolean} errorMask (true|false) default false
9614      */
9615     errorMask : false,
9616     
9617     /**
9618      * @cfg {Number} maskOffset Default 100
9619      */
9620     maskOffset : 100,
9621     
9622     /**
9623      * @cfg {Boolean} maskBody
9624      */
9625     maskBody : false,
9626
9627     getAutoCreate : function(){
9628
9629         var cfg = {
9630             tag: 'form',
9631             method : this.method || 'POST',
9632             id : this.id || Roo.id(),
9633             cls : ''
9634         };
9635         if (this.parent().xtype.match(/^Nav/)) {
9636             cfg.cls = 'navbar-form form-inline navbar-' + this.align;
9637
9638         }
9639
9640         if (this.labelAlign == 'left' ) {
9641             cfg.cls += ' form-horizontal';
9642         }
9643
9644
9645         return cfg;
9646     },
9647     initEvents : function()
9648     {
9649         this.el.on('submit', this.onSubmit, this);
9650         // this was added as random key presses on the form where triggering form submit.
9651         this.el.on('keypress', function(e) {
9652             if (e.getCharCode() != 13) {
9653                 return true;
9654             }
9655             // we might need to allow it for textareas.. and some other items.
9656             // check e.getTarget().
9657
9658             if(e.getTarget().nodeName.toLowerCase() === 'textarea'){
9659                 return true;
9660             }
9661
9662             Roo.log("keypress blocked");
9663
9664             e.preventDefault();
9665             return false;
9666         });
9667         
9668     },
9669     // private
9670     onSubmit : function(e){
9671         e.stopEvent();
9672     },
9673
9674      /**
9675      * Returns true if client-side validation on the form is successful.
9676      * @return Boolean
9677      */
9678     isValid : function(){
9679         var items = this.getItems();
9680         var valid = true;
9681         var target = false;
9682         
9683         items.each(function(f){
9684             
9685             if(f.validate()){
9686                 return;
9687             }
9688             
9689             Roo.log('invalid field: ' + f.name);
9690             
9691             valid = false;
9692
9693             if(!target && f.el.isVisible(true)){
9694                 target = f;
9695             }
9696            
9697         });
9698         
9699         if(this.errorMask && !valid){
9700             Roo.bootstrap.Form.popover.mask(this, target);
9701         }
9702         
9703         return valid;
9704     },
9705     
9706     /**
9707      * Returns true if any fields in this form have changed since their original load.
9708      * @return Boolean
9709      */
9710     isDirty : function(){
9711         var dirty = false;
9712         var items = this.getItems();
9713         items.each(function(f){
9714            if(f.isDirty()){
9715                dirty = true;
9716                return false;
9717            }
9718            return true;
9719         });
9720         return dirty;
9721     },
9722      /**
9723      * Performs a predefined action (submit or load) or custom actions you define on this form.
9724      * @param {String} actionName The name of the action type
9725      * @param {Object} options (optional) The options to pass to the action.  All of the config options listed
9726      * below are supported by both the submit and load actions unless otherwise noted (custom actions could also
9727      * accept other config options):
9728      * <pre>
9729 Property          Type             Description
9730 ----------------  ---------------  ----------------------------------------------------------------------------------
9731 url               String           The url for the action (defaults to the form's url)
9732 method            String           The form method to use (defaults to the form's method, or POST if not defined)
9733 params            String/Object    The params to pass (defaults to the form's baseParams, or none if not defined)
9734 clientValidation  Boolean          Applies to submit only.  Pass true to call form.isValid() prior to posting to
9735                                    validate the form on the client (defaults to false)
9736      * </pre>
9737      * @return {BasicForm} this
9738      */
9739     doAction : function(action, options){
9740         if(typeof action == 'string'){
9741             action = new Roo.form.Action.ACTION_TYPES[action](this, options);
9742         }
9743         if(this.fireEvent('beforeaction', this, action) !== false){
9744             this.beforeAction(action);
9745             action.run.defer(100, action);
9746         }
9747         return this;
9748     },
9749
9750     // private
9751     beforeAction : function(action){
9752         var o = action.options;
9753         
9754         if(this.loadMask){
9755             
9756             if(this.maskBody){
9757                 Roo.get(document.body).mask(o.waitMsg || "Sending", 'x-mask-loading')
9758             } else {
9759                 this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9760             }
9761         }
9762         // not really supported yet.. ??
9763
9764         //if(this.waitMsgTarget === true){
9765         //  this.el.mask(o.waitMsg || "Sending", 'x-mask-loading');
9766         //}else if(this.waitMsgTarget){
9767         //    this.waitMsgTarget = Roo.get(this.waitMsgTarget);
9768         //    this.waitMsgTarget.mask(o.waitMsg || "Sending", 'x-mask-loading');
9769         //}else {
9770         //    Roo.MessageBox.wait(o.waitMsg || "Sending", o.waitTitle || this.waitTitle || 'Please Wait...');
9771        // }
9772
9773     },
9774
9775     // private
9776     afterAction : function(action, success){
9777         this.activeAction = null;
9778         var o = action.options;
9779
9780         if(this.loadMask){
9781             
9782             if(this.maskBody){
9783                 Roo.get(document.body).unmask();
9784             } else {
9785                 this.el.unmask();
9786             }
9787         }
9788         
9789         //if(this.waitMsgTarget === true){
9790 //            this.el.unmask();
9791         //}else if(this.waitMsgTarget){
9792         //    this.waitMsgTarget.unmask();
9793         //}else{
9794         //    Roo.MessageBox.updateProgress(1);
9795         //    Roo.MessageBox.hide();
9796        // }
9797         //
9798         if(success){
9799             if(o.reset){
9800                 this.reset();
9801             }
9802             Roo.callback(o.success, o.scope, [this, action]);
9803             this.fireEvent('actioncomplete', this, action);
9804
9805         }else{
9806
9807             // failure condition..
9808             // we have a scenario where updates need confirming.
9809             // eg. if a locking scenario exists..
9810             // we look for { errors : { needs_confirm : true }} in the response.
9811             if (
9812                 (typeof(action.result) != 'undefined')  &&
9813                 (typeof(action.result.errors) != 'undefined')  &&
9814                 (typeof(action.result.errors.needs_confirm) != 'undefined')
9815            ){
9816                 var _t = this;
9817                 Roo.log("not supported yet");
9818                  /*
9819
9820                 Roo.MessageBox.confirm(
9821                     "Change requires confirmation",
9822                     action.result.errorMsg,
9823                     function(r) {
9824                         if (r != 'yes') {
9825                             return;
9826                         }
9827                         _t.doAction('submit', { params :  { _submit_confirmed : 1 } }  );
9828                     }
9829
9830                 );
9831                 */
9832
9833
9834                 return;
9835             }
9836
9837             Roo.callback(o.failure, o.scope, [this, action]);
9838             // show an error message if no failed handler is set..
9839             if (!this.hasListener('actionfailed')) {
9840                 Roo.log("need to add dialog support");
9841                 /*
9842                 Roo.MessageBox.alert("Error",
9843                     (typeof(action.result) != 'undefined' && typeof(action.result.errorMsg) != 'undefined') ?
9844                         action.result.errorMsg :
9845                         "Saving Failed, please check your entries or try again"
9846                 );
9847                 */
9848             }
9849
9850             this.fireEvent('actionfailed', this, action);
9851         }
9852
9853     },
9854     /**
9855      * Find a Roo.form.Field in this form by id, dataIndex, name or hiddenName
9856      * @param {String} id The value to search for
9857      * @return Field
9858      */
9859     findField : function(id){
9860         var items = this.getItems();
9861         var field = items.get(id);
9862         if(!field){
9863              items.each(function(f){
9864                 if(f.isFormField && (f.dataIndex == id || f.id == id || f.getName() == id)){
9865                     field = f;
9866                     return false;
9867                 }
9868                 return true;
9869             });
9870         }
9871         return field || null;
9872     },
9873      /**
9874      * Mark fields in this form invalid in bulk.
9875      * @param {Array/Object} errors Either an array in the form [{id:'fieldId', msg:'The message'},...] or an object hash of {id: msg, id2: msg2}
9876      * @return {BasicForm} this
9877      */
9878     markInvalid : function(errors){
9879         if(errors instanceof Array){
9880             for(var i = 0, len = errors.length; i < len; i++){
9881                 var fieldError = errors[i];
9882                 var f = this.findField(fieldError.id);
9883                 if(f){
9884                     f.markInvalid(fieldError.msg);
9885                 }
9886             }
9887         }else{
9888             var field, id;
9889             for(id in errors){
9890                 if(typeof errors[id] != 'function' && (field = this.findField(id))){
9891                     field.markInvalid(errors[id]);
9892                 }
9893             }
9894         }
9895         //Roo.each(this.childForms || [], function (f) {
9896         //    f.markInvalid(errors);
9897         //});
9898
9899         return this;
9900     },
9901
9902     /**
9903      * Set values for fields in this form in bulk.
9904      * @param {Array/Object} values Either an array in the form [{id:'fieldId', value:'foo'},...] or an object hash of {id: value, id2: value2}
9905      * @return {BasicForm} this
9906      */
9907     setValues : function(values){
9908         if(values instanceof Array){ // array of objects
9909             for(var i = 0, len = values.length; i < len; i++){
9910                 var v = values[i];
9911                 var f = this.findField(v.id);
9912                 if(f){
9913                     f.setValue(v.value);
9914                     if(this.trackResetOnLoad){
9915                         f.originalValue = f.getValue();
9916                     }
9917                 }
9918             }
9919         }else{ // object hash
9920             var field, id;
9921             for(id in values){
9922                 if(typeof values[id] != 'function' && (field = this.findField(id))){
9923
9924                     if (field.setFromData &&
9925                         field.valueField &&
9926                         field.displayField &&
9927                         // combos' with local stores can
9928                         // be queried via setValue()
9929                         // to set their value..
9930                         (field.store && !field.store.isLocal)
9931                         ) {
9932                         // it's a combo
9933                         var sd = { };
9934                         sd[field.valueField] = typeof(values[field.hiddenName]) == 'undefined' ? '' : values[field.hiddenName];
9935                         sd[field.displayField] = typeof(values[field.name]) == 'undefined' ? '' : values[field.name];
9936                         field.setFromData(sd);
9937
9938                     } else if(field.setFromData && (field.store && !field.store.isLocal)) {
9939                         
9940                         field.setFromData(values);
9941                         
9942                     } else {
9943                         field.setValue(values[id]);
9944                     }
9945
9946
9947                     if(this.trackResetOnLoad){
9948                         field.originalValue = field.getValue();
9949                     }
9950                 }
9951             }
9952         }
9953
9954         //Roo.each(this.childForms || [], function (f) {
9955         //    f.setValues(values);
9956         //});
9957
9958         return this;
9959     },
9960
9961     /**
9962      * Returns the fields in this form as an object with key/value pairs. If multiple fields exist with the same name
9963      * they are returned as an array.
9964      * @param {Boolean} asString
9965      * @return {Object}
9966      */
9967     getValues : function(asString){
9968         //if (this.childForms) {
9969             // copy values from the child forms
9970         //    Roo.each(this.childForms, function (f) {
9971         //        this.setValues(f.getValues());
9972         //    }, this);
9973         //}
9974
9975
9976
9977         var fs = Roo.lib.Ajax.serializeForm(this.el.dom);
9978         if(asString === true){
9979             return fs;
9980         }
9981         return Roo.urlDecode(fs);
9982     },
9983
9984     /**
9985      * Returns the fields in this form as an object with key/value pairs.
9986      * This differs from getValues as it calls getValue on each child item, rather than using dom data.
9987      * @return {Object}
9988      */
9989     getFieldValues : function(with_hidden)
9990     {
9991         var items = this.getItems();
9992         var ret = {};
9993         items.each(function(f){
9994             
9995             if (!f.getName()) {
9996                 return;
9997             }
9998             
9999             var v = f.getValue();
10000             
10001             if (f.inputType =='radio') {
10002                 if (typeof(ret[f.getName()]) == 'undefined') {
10003                     ret[f.getName()] = ''; // empty..
10004                 }
10005
10006                 if (!f.el.dom.checked) {
10007                     return;
10008
10009                 }
10010                 v = f.el.dom.value;
10011
10012             }
10013             
10014             if(f.xtype == 'MoneyField'){
10015                 ret[f.currencyName] = f.getCurrency();
10016             }
10017
10018             // not sure if this supported any more..
10019             if ((typeof(v) == 'object') && f.getRawValue) {
10020                 v = f.getRawValue() ; // dates..
10021             }
10022             // combo boxes where name != hiddenName...
10023             if (f.name !== false && f.name != '' && f.name != f.getName()) {
10024                 ret[f.name] = f.getRawValue();
10025             }
10026             ret[f.getName()] = v;
10027         });
10028
10029         return ret;
10030     },
10031
10032     /**
10033      * Clears all invalid messages in this form.
10034      * @return {BasicForm} this
10035      */
10036     clearInvalid : function(){
10037         var items = this.getItems();
10038
10039         items.each(function(f){
10040            f.clearInvalid();
10041         });
10042
10043         return this;
10044     },
10045
10046     /**
10047      * Resets this form.
10048      * @return {BasicForm} this
10049      */
10050     reset : function(){
10051         var items = this.getItems();
10052         items.each(function(f){
10053             f.reset();
10054         });
10055
10056         Roo.each(this.childForms || [], function (f) {
10057             f.reset();
10058         });
10059
10060
10061         return this;
10062     },
10063     
10064     getItems : function()
10065     {
10066         var r=new Roo.util.MixedCollection(false, function(o){
10067             return o.id || (o.id = Roo.id());
10068         });
10069         var iter = function(el) {
10070             if (el.inputEl) {
10071                 r.add(el);
10072             }
10073             if (!el.items) {
10074                 return;
10075             }
10076             Roo.each(el.items,function(e) {
10077                 iter(e);
10078             });
10079         };
10080
10081         iter(this);
10082         return r;
10083     },
10084     
10085     hideFields : function(items)
10086     {
10087         Roo.each(items, function(i){
10088             
10089             var f = this.findField(i);
10090             
10091             if(!f){
10092                 return;
10093             }
10094             
10095             f.hide();
10096             
10097         }, this);
10098     },
10099     
10100     showFields : function(items)
10101     {
10102         Roo.each(items, function(i){
10103             
10104             var f = this.findField(i);
10105             
10106             if(!f){
10107                 return;
10108             }
10109             
10110             f.show();
10111             
10112         }, this);
10113     }
10114
10115 });
10116
10117 Roo.apply(Roo.bootstrap.Form, {
10118     
10119     popover : {
10120         
10121         padding : 5,
10122         
10123         isApplied : false,
10124         
10125         isMasked : false,
10126         
10127         form : false,
10128         
10129         target : false,
10130         
10131         toolTip : false,
10132         
10133         intervalID : false,
10134         
10135         maskEl : false,
10136         
10137         apply : function()
10138         {
10139             if(this.isApplied){
10140                 return;
10141             }
10142             
10143             this.maskEl = {
10144                 top : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-top-mask" }, true),
10145                 left : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-left-mask" }, true),
10146                 bottom : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-bottom-mask" }, true),
10147                 right : Roo.DomHelper.append(Roo.get(document.body), { tag: "div", cls:"x-dlg-mask roo-form-right-mask" }, true)
10148             };
10149             
10150             this.maskEl.top.enableDisplayMode("block");
10151             this.maskEl.left.enableDisplayMode("block");
10152             this.maskEl.bottom.enableDisplayMode("block");
10153             this.maskEl.right.enableDisplayMode("block");
10154             
10155             this.toolTip = new Roo.bootstrap.Tooltip({
10156                 cls : 'roo-form-error-popover',
10157                 alignment : {
10158                     'left' : ['r-l', [-2,0], 'right'],
10159                     'right' : ['l-r', [2,0], 'left'],
10160                     'bottom' : ['tl-bl', [0,2], 'top'],
10161                     'top' : [ 'bl-tl', [0,-2], 'bottom']
10162                 }
10163             });
10164             
10165             this.toolTip.render(Roo.get(document.body));
10166
10167             this.toolTip.el.enableDisplayMode("block");
10168             
10169             Roo.get(document.body).on('click', function(){
10170                 this.unmask();
10171             }, this);
10172             
10173             Roo.get(document.body).on('touchstart', function(){
10174                 this.unmask();
10175             }, this);
10176             
10177             this.isApplied = true
10178         },
10179         
10180         mask : function(form, target)
10181         {
10182             this.form = form;
10183             
10184             this.target = target;
10185             
10186             if(!this.form.errorMask || !target.el){
10187                 return;
10188             }
10189             
10190             var scrollable = this.target.el.findScrollableParent() || this.target.el.findParent('div.modal', 100, true) || Roo.get(document.body);
10191             
10192             Roo.log(scrollable);
10193             
10194             var ot = this.target.el.calcOffsetsTo(scrollable);
10195             
10196             var scrollTo = ot[1] - this.form.maskOffset;
10197             
10198             scrollTo = Math.min(scrollTo, scrollable.dom.scrollHeight);
10199             
10200             scrollable.scrollTo('top', scrollTo);
10201             
10202             var box = this.target.el.getBox();
10203             Roo.log(box);
10204             var zIndex = Roo.bootstrap.Modal.zIndex++;
10205
10206             
10207             this.maskEl.top.setStyle('position', 'absolute');
10208             this.maskEl.top.setStyle('z-index', zIndex);
10209             this.maskEl.top.setSize(Roo.lib.Dom.getDocumentWidth(), box.y - this.padding);
10210             this.maskEl.top.setLeft(0);
10211             this.maskEl.top.setTop(0);
10212             this.maskEl.top.show();
10213             
10214             this.maskEl.left.setStyle('position', 'absolute');
10215             this.maskEl.left.setStyle('z-index', zIndex);
10216             this.maskEl.left.setSize(box.x - this.padding, box.height + this.padding * 2);
10217             this.maskEl.left.setLeft(0);
10218             this.maskEl.left.setTop(box.y - this.padding);
10219             this.maskEl.left.show();
10220
10221             this.maskEl.bottom.setStyle('position', 'absolute');
10222             this.maskEl.bottom.setStyle('z-index', zIndex);
10223             this.maskEl.bottom.setSize(Roo.lib.Dom.getDocumentWidth(), Roo.lib.Dom.getDocumentHeight() - box.bottom - this.padding);
10224             this.maskEl.bottom.setLeft(0);
10225             this.maskEl.bottom.setTop(box.bottom + this.padding);
10226             this.maskEl.bottom.show();
10227
10228             this.maskEl.right.setStyle('position', 'absolute');
10229             this.maskEl.right.setStyle('z-index', zIndex);
10230             this.maskEl.right.setSize(Roo.lib.Dom.getDocumentWidth() - box.right - this.padding, box.height + this.padding * 2);
10231             this.maskEl.right.setLeft(box.right + this.padding);
10232             this.maskEl.right.setTop(box.y - this.padding);
10233             this.maskEl.right.show();
10234
10235             this.toolTip.bindEl = this.target.el;
10236
10237             this.toolTip.el.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
10238
10239             var tip = this.target.blankText;
10240
10241             if(this.target.getValue() !== '' ) {
10242                 
10243                 if (this.target.invalidText.length) {
10244                     tip = this.target.invalidText;
10245                 } else if (this.target.regexText.length){
10246                     tip = this.target.regexText;
10247                 }
10248             }
10249
10250             this.toolTip.show(tip);
10251
10252             this.intervalID = window.setInterval(function() {
10253                 Roo.bootstrap.Form.popover.unmask();
10254             }, 10000);
10255
10256             window.onwheel = function(){ return false;};
10257             
10258             (function(){ this.isMasked = true; }).defer(500, this);
10259             
10260         },
10261         
10262         unmask : function()
10263         {
10264             if(!this.isApplied || !this.isMasked || !this.form || !this.target || !this.form.errorMask){
10265                 return;
10266             }
10267             
10268             this.maskEl.top.setStyle('position', 'absolute');
10269             this.maskEl.top.setSize(0, 0).setXY([0, 0]);
10270             this.maskEl.top.hide();
10271
10272             this.maskEl.left.setStyle('position', 'absolute');
10273             this.maskEl.left.setSize(0, 0).setXY([0, 0]);
10274             this.maskEl.left.hide();
10275
10276             this.maskEl.bottom.setStyle('position', 'absolute');
10277             this.maskEl.bottom.setSize(0, 0).setXY([0, 0]);
10278             this.maskEl.bottom.hide();
10279
10280             this.maskEl.right.setStyle('position', 'absolute');
10281             this.maskEl.right.setSize(0, 0).setXY([0, 0]);
10282             this.maskEl.right.hide();
10283             
10284             this.toolTip.hide();
10285             
10286             this.toolTip.el.hide();
10287             
10288             window.onwheel = function(){ return true;};
10289             
10290             if(this.intervalID){
10291                 window.clearInterval(this.intervalID);
10292                 this.intervalID = false;
10293             }
10294             
10295             this.isMasked = false;
10296             
10297         }
10298         
10299     }
10300     
10301 });
10302
10303 /*
10304  * Based on:
10305  * Ext JS Library 1.1.1
10306  * Copyright(c) 2006-2007, Ext JS, LLC.
10307  *
10308  * Originally Released Under LGPL - original licence link has changed is not relivant.
10309  *
10310  * Fork - LGPL
10311  * <script type="text/javascript">
10312  */
10313 /**
10314  * @class Roo.form.VTypes
10315  * Overridable validation definitions. The validations provided are basic and intended to be easily customizable and extended.
10316  * @singleton
10317  */
10318 Roo.form.VTypes = function(){
10319     // closure these in so they are only created once.
10320     var alpha = /^[a-zA-Z_]+$/;
10321     var alphanum = /^[a-zA-Z0-9_]+$/;
10322     var email = /^([\w]+)(.[\w]+)*@([\w-]+\.){1,5}([A-Za-z]){2,24}$/;
10323     var url = /(((https?)|(ftp)):\/\/([\-\w]+\.)+\w{2,3}(\/[%\-\w]+(\.\w{2,})?)*(([\w\-\.\?\\\/+@&#;`~=%!]*)(\.\w{2,})?)*\/?)/i;
10324
10325     // All these messages and functions are configurable
10326     return {
10327         /**
10328          * The function used to validate email addresses
10329          * @param {String} value The email address
10330          */
10331         'email' : function(v){
10332             return email.test(v);
10333         },
10334         /**
10335          * The error text to display when the email validation function returns false
10336          * @type String
10337          */
10338         'emailText' : 'This field should be an e-mail address in the format "user@domain.com"',
10339         /**
10340          * The keystroke filter mask to be applied on email input
10341          * @type RegExp
10342          */
10343         'emailMask' : /[a-z0-9_\.\-@]/i,
10344
10345         /**
10346          * The function used to validate URLs
10347          * @param {String} value The URL
10348          */
10349         'url' : function(v){
10350             return url.test(v);
10351         },
10352         /**
10353          * The error text to display when the url validation function returns false
10354          * @type String
10355          */
10356         'urlText' : 'This field should be a URL in the format "http:/'+'/www.domain.com"',
10357         
10358         /**
10359          * The function used to validate alpha values
10360          * @param {String} value The value
10361          */
10362         'alpha' : function(v){
10363             return alpha.test(v);
10364         },
10365         /**
10366          * The error text to display when the alpha validation function returns false
10367          * @type String
10368          */
10369         'alphaText' : 'This field should only contain letters and _',
10370         /**
10371          * The keystroke filter mask to be applied on alpha input
10372          * @type RegExp
10373          */
10374         'alphaMask' : /[a-z_]/i,
10375
10376         /**
10377          * The function used to validate alphanumeric values
10378          * @param {String} value The value
10379          */
10380         'alphanum' : function(v){
10381             return alphanum.test(v);
10382         },
10383         /**
10384          * The error text to display when the alphanumeric validation function returns false
10385          * @type String
10386          */
10387         'alphanumText' : 'This field should only contain letters, numbers and _',
10388         /**
10389          * The keystroke filter mask to be applied on alphanumeric input
10390          * @type RegExp
10391          */
10392         'alphanumMask' : /[a-z0-9_]/i
10393     };
10394 }();/*
10395  * - LGPL
10396  *
10397  * Input
10398  * 
10399  */
10400
10401 /**
10402  * @class Roo.bootstrap.Input
10403  * @extends Roo.bootstrap.Component
10404  * Bootstrap Input class
10405  * @cfg {Boolean} disabled is it disabled
10406  * @cfg {String} (button|checkbox|email|file|hidden|image|number|password|radio|range|reset|search|submit|text) inputType 
10407  * @cfg {String} name name of the input
10408  * @cfg {string} fieldLabel - the label associated
10409  * @cfg {string} placeholder - placeholder to put in text.
10410  * @cfg {string}  before - input group add on before
10411  * @cfg {string} after - input group add on after
10412  * @cfg {string} size - (lg|sm) or leave empty..
10413  * @cfg {Number} xs colspan out of 12 for mobile-sized screens
10414  * @cfg {Number} sm colspan out of 12 for tablet-sized screens
10415  * @cfg {Number} md colspan out of 12 for computer-sized screens
10416  * @cfg {Number} lg colspan out of 12 for large computer-sized screens
10417  * @cfg {string} value default value of the input
10418  * @cfg {Number} labelWidth set the width of label 
10419  * @cfg {Number} labellg set the width of label (1-12)
10420  * @cfg {Number} labelmd set the width of label (1-12)
10421  * @cfg {Number} labelsm set the width of label (1-12)
10422  * @cfg {Number} labelxs set the width of label (1-12)
10423  * @cfg {String} labelAlign (top|left)
10424  * @cfg {Boolean} readOnly Specifies that the field should be read-only
10425  * @cfg {String} autocomplete - default is new-password see: https://developers.google.com/web/fundamentals/input/form/label-and-name-inputs?hl=en
10426  * @cfg {String} indicatorpos (left|right) default left
10427  * @cfg {String} capture (user|camera) use for file input only. (default empty)
10428  * @cfg {String} accept (image|video|audio) use for file input only. (default empty)
10429  * @cfg {Boolean} preventMark Do not show tick or cross if error/success
10430
10431  * @cfg {String} align (left|center|right) Default left
10432  * @cfg {Boolean} forceFeedback (true|false) Default false
10433  * 
10434  * @constructor
10435  * Create a new Input
10436  * @param {Object} config The config object
10437  */
10438
10439 Roo.bootstrap.Input = function(config){
10440     
10441     Roo.bootstrap.Input.superclass.constructor.call(this, config);
10442     
10443     this.addEvents({
10444         /**
10445          * @event focus
10446          * Fires when this field receives input focus.
10447          * @param {Roo.form.Field} this
10448          */
10449         focus : true,
10450         /**
10451          * @event blur
10452          * Fires when this field loses input focus.
10453          * @param {Roo.form.Field} this
10454          */
10455         blur : true,
10456         /**
10457          * @event specialkey
10458          * Fires when any key related to navigation (arrows, tab, enter, esc, etc.) is pressed.  You can check
10459          * {@link Roo.EventObject#getKey} to determine which key was pressed.
10460          * @param {Roo.form.Field} this
10461          * @param {Roo.EventObject} e The event object
10462          */
10463         specialkey : true,
10464         /**
10465          * @event change
10466          * Fires just before the field blurs if the field value has changed.
10467          * @param {Roo.form.Field} this
10468          * @param {Mixed} newValue The new value
10469          * @param {Mixed} oldValue The original value
10470          */
10471         change : true,
10472         /**
10473          * @event invalid
10474          * Fires after the field has been marked as invalid.
10475          * @param {Roo.form.Field} this
10476          * @param {String} msg The validation message
10477          */
10478         invalid : true,
10479         /**
10480          * @event valid
10481          * Fires after the field has been validated with no errors.
10482          * @param {Roo.form.Field} this
10483          */
10484         valid : true,
10485          /**
10486          * @event keyup
10487          * Fires after the key up
10488          * @param {Roo.form.Field} this
10489          * @param {Roo.EventObject}  e The event Object
10490          */
10491         keyup : true
10492     });
10493 };
10494
10495 Roo.extend(Roo.bootstrap.Input, Roo.bootstrap.Component,  {
10496      /**
10497      * @cfg {String/Boolean} validationEvent The event that should initiate field validation. Set to false to disable
10498       automatic validation (defaults to "keyup").
10499      */
10500     validationEvent : "keyup",
10501      /**
10502      * @cfg {Boolean} validateOnBlur Whether the field should validate when it loses focus (defaults to true).
10503      */
10504     validateOnBlur : true,
10505     /**
10506      * @cfg {Number} validationDelay The length of time in milliseconds after user input begins until validation is initiated (defaults to 250)
10507      */
10508     validationDelay : 250,
10509      /**
10510      * @cfg {String} focusClass The CSS class to use when the field receives focus (defaults to "x-form-focus")
10511      */
10512     focusClass : "x-form-focus",  // not needed???
10513     
10514        
10515     /**
10516      * @cfg {String} invalidClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10517      */
10518     invalidClass : "has-warning",
10519     
10520     /**
10521      * @cfg {String} validClass DEPRICATED - code uses BS4 - is-valid / is-invalid
10522      */
10523     validClass : "has-success",
10524     
10525     /**
10526      * @cfg {Boolean} hasFeedback (true|false) default true
10527      */
10528     hasFeedback : true,
10529     
10530     /**
10531      * @cfg {String} invalidFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10532      */
10533     invalidFeedbackClass : "glyphicon-warning-sign",
10534     
10535     /**
10536      * @cfg {String} validFeedbackIcon The CSS class to use when create feedback icon (defaults to "x-form-invalid")
10537      */
10538     validFeedbackClass : "glyphicon-ok",
10539     
10540     /**
10541      * @cfg {Boolean} selectOnFocus True to automatically select any existing field text when the field receives input focus (defaults to false)
10542      */
10543     selectOnFocus : false,
10544     
10545      /**
10546      * @cfg {String} maskRe An input mask regular expression that will be used to filter keystrokes that don't match (defaults to null)
10547      */
10548     maskRe : null,
10549        /**
10550      * @cfg {String} vtype A validation type name as defined in {@link Roo.form.VTypes} (defaults to null)
10551      */
10552     vtype : null,
10553     
10554       /**
10555      * @cfg {Boolean} disableKeyFilter True to disable input keystroke filtering (defaults to false)
10556      */
10557     disableKeyFilter : false,
10558     
10559        /**
10560      * @cfg {Boolean} disabled True to disable the field (defaults to false).
10561      */
10562     disabled : false,
10563      /**
10564      * @cfg {Boolean} allowBlank False to validate that the value length > 0 (defaults to true)
10565      */
10566     allowBlank : true,
10567     /**
10568      * @cfg {String} blankText Error text to display if the allow blank validation fails (defaults to "This field is required")
10569      */
10570     blankText : "Please complete this mandatory field",
10571     
10572      /**
10573      * @cfg {Number} minLength Minimum input field length required (defaults to 0)
10574      */
10575     minLength : 0,
10576     /**
10577      * @cfg {Number} maxLength Maximum input field length allowed (defaults to Number.MAX_VALUE)
10578      */
10579     maxLength : Number.MAX_VALUE,
10580     /**
10581      * @cfg {String} minLengthText Error text to display if the minimum length validation fails (defaults to "The minimum length for this field is {minLength}")
10582      */
10583     minLengthText : "The minimum length for this field is {0}",
10584     /**
10585      * @cfg {String} maxLengthText Error text to display if the maximum length validation fails (defaults to "The maximum length for this field is {maxLength}")
10586      */
10587     maxLengthText : "The maximum length for this field is {0}",
10588   
10589     
10590     /**
10591      * @cfg {Function} validator A custom validation function to be called during field validation (defaults to null).
10592      * If available, this function will be called only after the basic validators all return true, and will be passed the
10593      * current field value and expected to return boolean true if the value is valid or a string error message if invalid.
10594      */
10595     validator : null,
10596     /**
10597      * @cfg {RegExp} regex A JavaScript RegExp object to be tested against the field value during validation (defaults to null).
10598      * If available, this regex will be evaluated only after the basic validators all return true, and will be passed the
10599      * current field value.  If the test fails, the field will be marked invalid using {@link #regexText}.
10600      */
10601     regex : null,
10602     /**
10603      * @cfg {String} regexText -- Depricated - use Invalid Text
10604      */
10605     regexText : "",
10606     
10607     /**
10608      * @cfg {String} invalidText The error text to display if {@link #validator} test fails during validation (defaults to "")
10609      */
10610     invalidText : "",
10611     
10612     
10613     
10614     autocomplete: false,
10615     
10616     
10617     fieldLabel : '',
10618     inputType : 'text',
10619     
10620     name : false,
10621     placeholder: false,
10622     before : false,
10623     after : false,
10624     size : false,
10625     hasFocus : false,
10626     preventMark: false,
10627     isFormField : true,
10628     value : '',
10629     labelWidth : 2,
10630     labelAlign : false,
10631     readOnly : false,
10632     align : false,
10633     formatedValue : false,
10634     forceFeedback : false,
10635     
10636     indicatorpos : 'left',
10637     
10638     labellg : 0,
10639     labelmd : 0,
10640     labelsm : 0,
10641     labelxs : 0,
10642     
10643     capture : '',
10644     accept : '',
10645     
10646     parentLabelAlign : function()
10647     {
10648         var parent = this;
10649         while (parent.parent()) {
10650             parent = parent.parent();
10651             if (typeof(parent.labelAlign) !='undefined') {
10652                 return parent.labelAlign;
10653             }
10654         }
10655         return 'left';
10656         
10657     },
10658     
10659     getAutoCreate : function()
10660     {
10661         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
10662         
10663         var id = Roo.id();
10664         
10665         var cfg = {};
10666         
10667         if(this.inputType != 'hidden'){
10668             cfg.cls = 'form-group' //input-group
10669         }
10670         
10671         var input =  {
10672             tag: 'input',
10673             id : id,
10674             type : this.inputType,
10675             value : this.value,
10676             cls : 'form-control',
10677             placeholder : this.placeholder || '',
10678             autocomplete : this.autocomplete || 'new-password'
10679         };
10680         if (this.inputType == 'file') {
10681             input.style = 'overflow:hidden'; // why not in CSS?
10682         }
10683         
10684         if(this.capture.length){
10685             input.capture = this.capture;
10686         }
10687         
10688         if(this.accept.length){
10689             input.accept = this.accept + "/*";
10690         }
10691         
10692         if(this.align){
10693             input.style = (typeof(input.style) == 'undefined') ? ('text-align:' + this.align) : (input.style + 'text-align:' + this.align);
10694         }
10695         
10696         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
10697             input.maxLength = this.maxLength;
10698         }
10699         
10700         if (this.disabled) {
10701             input.disabled=true;
10702         }
10703         
10704         if (this.readOnly) {
10705             input.readonly=true;
10706         }
10707         
10708         if (this.name) {
10709             input.name = this.name;
10710         }
10711         
10712         if (this.size) {
10713             input.cls += ' input-' + this.size;
10714         }
10715         
10716         var settings=this;
10717         ['xs','sm','md','lg'].map(function(size){
10718             if (settings[size]) {
10719                 cfg.cls += ' col-' + size + '-' + settings[size];
10720             }
10721         });
10722         
10723         var inputblock = input;
10724         
10725         var feedback = {
10726             tag: 'span',
10727             cls: 'glyphicon form-control-feedback'
10728         };
10729             
10730         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10731             
10732             inputblock = {
10733                 cls : 'has-feedback',
10734                 cn :  [
10735                     input,
10736                     feedback
10737                 ] 
10738             };  
10739         }
10740         
10741         if (this.before || this.after) {
10742             
10743             inputblock = {
10744                 cls : 'input-group',
10745                 cn :  [] 
10746             };
10747             
10748             if (this.before && typeof(this.before) == 'string') {
10749                 
10750                 inputblock.cn.push({
10751                     tag :'span',
10752                     cls : 'roo-input-before input-group-addon input-group-prepend input-group-text',
10753                     html : this.before
10754                 });
10755             }
10756             if (this.before && typeof(this.before) == 'object') {
10757                 this.before = Roo.factory(this.before);
10758                 
10759                 inputblock.cn.push({
10760                     tag :'span',
10761                     cls : 'roo-input-before input-group-prepend   input-group-' +
10762                         (this.before.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10763                 });
10764             }
10765             
10766             inputblock.cn.push(input);
10767             
10768             if (this.after && typeof(this.after) == 'string') {
10769                 inputblock.cn.push({
10770                     tag :'span',
10771                     cls : 'roo-input-after input-group-append input-group-text input-group-addon',
10772                     html : this.after
10773                 });
10774             }
10775             if (this.after && typeof(this.after) == 'object') {
10776                 this.after = Roo.factory(this.after);
10777                 
10778                 inputblock.cn.push({
10779                     tag :'span',
10780                     cls : 'roo-input-after input-group-append  input-group-' +
10781                         (this.after.xtype == 'Button' ? 'btn' : 'addon')  //?? what about checkboxes - that looks like a bit of a hack thought? 
10782                 });
10783             }
10784             
10785             if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
10786                 inputblock.cls += ' has-feedback';
10787                 inputblock.cn.push(feedback);
10788             }
10789         };
10790         var indicator = {
10791             tag : 'i',
10792             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
10793             tooltip : 'This field is required'
10794         };
10795         if (this.allowBlank ) {
10796             indicator.style = this.allowBlank ? ' display:none' : '';
10797         }
10798         if (align ==='left' && this.fieldLabel.length) {
10799             
10800             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
10801             
10802             cfg.cn = [
10803                 indicator,
10804                 {
10805                     tag: 'label',
10806                     'for' :  id,
10807                     cls : 'control-label col-form-label',
10808                     html : this.fieldLabel
10809
10810                 },
10811                 {
10812                     cls : "", 
10813                     cn: [
10814                         inputblock
10815                     ]
10816                 }
10817             ];
10818             
10819             var labelCfg = cfg.cn[1];
10820             var contentCfg = cfg.cn[2];
10821             
10822             if(this.indicatorpos == 'right'){
10823                 cfg.cn = [
10824                     {
10825                         tag: 'label',
10826                         'for' :  id,
10827                         cls : 'control-label col-form-label',
10828                         cn : [
10829                             {
10830                                 tag : 'span',
10831                                 html : this.fieldLabel
10832                             },
10833                             indicator
10834                         ]
10835                     },
10836                     {
10837                         cls : "",
10838                         cn: [
10839                             inputblock
10840                         ]
10841                     }
10842
10843                 ];
10844                 
10845                 labelCfg = cfg.cn[0];
10846                 contentCfg = cfg.cn[1];
10847             
10848             }
10849             
10850             if(this.labelWidth > 12){
10851                 labelCfg.style = "width: " + this.labelWidth + 'px';
10852             }
10853             
10854             if(this.labelWidth < 13 && this.labelmd == 0){
10855                 this.labelmd = this.labelWidth;
10856             }
10857             
10858             if(this.labellg > 0){
10859                 labelCfg.cls += ' col-lg-' + this.labellg;
10860                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
10861             }
10862             
10863             if(this.labelmd > 0){
10864                 labelCfg.cls += ' col-md-' + this.labelmd;
10865                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
10866             }
10867             
10868             if(this.labelsm > 0){
10869                 labelCfg.cls += ' col-sm-' + this.labelsm;
10870                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
10871             }
10872             
10873             if(this.labelxs > 0){
10874                 labelCfg.cls += ' col-xs-' + this.labelxs;
10875                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
10876             }
10877             
10878             
10879         } else if ( this.fieldLabel.length) {
10880                 
10881             
10882             
10883             cfg.cn = [
10884                 {
10885                     tag : 'i',
10886                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
10887                     tooltip : 'This field is required',
10888                     style : this.allowBlank ? ' display:none' : '' 
10889                 },
10890                 {
10891                     tag: 'label',
10892                    //cls : 'input-group-addon',
10893                     html : this.fieldLabel
10894
10895                 },
10896
10897                inputblock
10898
10899            ];
10900            
10901            if(this.indicatorpos == 'right'){
10902        
10903                 cfg.cn = [
10904                     {
10905                         tag: 'label',
10906                        //cls : 'input-group-addon',
10907                         html : this.fieldLabel
10908
10909                     },
10910                     {
10911                         tag : 'i',
10912                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
10913                         tooltip : 'This field is required',
10914                         style : this.allowBlank ? ' display:none' : '' 
10915                     },
10916
10917                    inputblock
10918
10919                ];
10920
10921             }
10922
10923         } else {
10924             
10925             cfg.cn = [
10926
10927                     inputblock
10928
10929             ];
10930                 
10931                 
10932         };
10933         
10934         if (this.parentType === 'Navbar' &&  this.parent().bar) {
10935            cfg.cls += ' navbar-form';
10936         }
10937         
10938         if (this.parentType === 'NavGroup' && !(Roo.bootstrap.version == 4 && this.parent().form)) {
10939             // on BS4 we do this only if not form 
10940             cfg.cls += ' navbar-form';
10941             cfg.tag = 'li';
10942         }
10943         
10944         return cfg;
10945         
10946     },
10947     /**
10948      * return the real input element.
10949      */
10950     inputEl: function ()
10951     {
10952         return this.el.select('input.form-control',true).first();
10953     },
10954     
10955     tooltipEl : function()
10956     {
10957         return this.inputEl();
10958     },
10959     
10960     indicatorEl : function()
10961     {
10962         if (Roo.bootstrap.version == 4) {
10963             return false; // not enabled in v4 yet.
10964         }
10965         
10966         var indicator = this.el.select('i.roo-required-indicator',true).first();
10967         
10968         if(!indicator){
10969             return false;
10970         }
10971         
10972         return indicator;
10973         
10974     },
10975     
10976     setDisabled : function(v)
10977     {
10978         var i  = this.inputEl().dom;
10979         if (!v) {
10980             i.removeAttribute('disabled');
10981             return;
10982             
10983         }
10984         i.setAttribute('disabled','true');
10985     },
10986     initEvents : function()
10987     {
10988           
10989         this.inputEl().on("keydown" , this.fireKey,  this);
10990         this.inputEl().on("focus", this.onFocus,  this);
10991         this.inputEl().on("blur", this.onBlur,  this);
10992         
10993         this.inputEl().relayEvent('keyup', this);
10994         
10995         this.indicator = this.indicatorEl();
10996         
10997         if(this.indicator){
10998             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible'); // changed from invisible??? - 
10999         }
11000  
11001         // reference to original value for reset
11002         this.originalValue = this.getValue();
11003         //Roo.form.TextField.superclass.initEvents.call(this);
11004         if(this.validationEvent == 'keyup'){
11005             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
11006             this.inputEl().on('keyup', this.filterValidation, this);
11007         }
11008         else if(this.validationEvent !== false){
11009             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
11010         }
11011         
11012         if(this.selectOnFocus){
11013             this.on("focus", this.preFocus, this);
11014             
11015         }
11016         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
11017             this.inputEl().on("keypress", this.filterKeys, this);
11018         } else {
11019             this.inputEl().relayEvent('keypress', this);
11020         }
11021        /* if(this.grow){
11022             this.el.on("keyup", this.onKeyUp,  this, {buffer:50});
11023             this.el.on("click", this.autoSize,  this);
11024         }
11025         */
11026         if(this.inputEl().is('input[type=password]') && Roo.isSafari){
11027             this.inputEl().on('keydown', this.SafariOnKeyDown, this);
11028         }
11029         
11030         if (typeof(this.before) == 'object') {
11031             this.before.render(this.el.select('.roo-input-before',true).first());
11032         }
11033         if (typeof(this.after) == 'object') {
11034             this.after.render(this.el.select('.roo-input-after',true).first());
11035         }
11036         
11037         this.inputEl().on('change', this.onChange, this);
11038         
11039     },
11040     filterValidation : function(e){
11041         if(!e.isNavKeyPress()){
11042             this.validationTask.delay(this.validationDelay);
11043         }
11044     },
11045      /**
11046      * Validates the field value
11047      * @return {Boolean} True if the value is valid, else false
11048      */
11049     validate : function(){
11050         //if(this.disabled || this.validateValue(this.processValue(this.getRawValue()))){
11051         if(this.disabled || this.validateValue(this.getRawValue())){
11052             this.markValid();
11053             return true;
11054         }
11055         
11056         this.markInvalid();
11057         return false;
11058     },
11059     
11060     
11061     /**
11062      * Validates a value according to the field's validation rules and marks the field as invalid
11063      * if the validation fails
11064      * @param {Mixed} value The value to validate
11065      * @return {Boolean} True if the value is valid, else false
11066      */
11067     validateValue : function(value)
11068     {
11069         if(this.getVisibilityEl().hasClass('hidden')){
11070             return true;
11071         }
11072         
11073         if(value.length < 1)  { // if it's blank
11074             if(this.allowBlank){
11075                 return true;
11076             }
11077             return false;
11078         }
11079         
11080         if(value.length < this.minLength){
11081             return false;
11082         }
11083         if(value.length > this.maxLength){
11084             return false;
11085         }
11086         if(this.vtype){
11087             var vt = Roo.form.VTypes;
11088             if(!vt[this.vtype](value, this)){
11089                 return false;
11090             }
11091         }
11092         if(typeof this.validator == "function"){
11093             var msg = this.validator(value);
11094             if(msg !== true){
11095                 return false;
11096             }
11097             if (typeof(msg) == 'string') {
11098                 this.invalidText = msg;
11099             }
11100         }
11101         
11102         if(this.regex && !this.regex.test(value)){
11103             return false;
11104         }
11105         
11106         return true;
11107     },
11108     
11109      // private
11110     fireKey : function(e){
11111         //Roo.log('field ' + e.getKey());
11112         if(e.isNavKeyPress()){
11113             this.fireEvent("specialkey", this, e);
11114         }
11115     },
11116     focus : function (selectText){
11117         if(this.rendered){
11118             this.inputEl().focus();
11119             if(selectText === true){
11120                 this.inputEl().dom.select();
11121             }
11122         }
11123         return this;
11124     } ,
11125     
11126     onFocus : function(){
11127         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11128            // this.el.addClass(this.focusClass);
11129         }
11130         if(!this.hasFocus){
11131             this.hasFocus = true;
11132             this.startValue = this.getValue();
11133             this.fireEvent("focus", this);
11134         }
11135     },
11136     
11137     beforeBlur : Roo.emptyFn,
11138
11139     
11140     // private
11141     onBlur : function(){
11142         this.beforeBlur();
11143         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
11144             //this.el.removeClass(this.focusClass);
11145         }
11146         this.hasFocus = false;
11147         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
11148             this.validate();
11149         }
11150         var v = this.getValue();
11151         if(String(v) !== String(this.startValue)){
11152             this.fireEvent('change', this, v, this.startValue);
11153         }
11154         this.fireEvent("blur", this);
11155     },
11156     
11157     onChange : function(e)
11158     {
11159         var v = this.getValue();
11160         if(String(v) !== String(this.startValue)){
11161             this.fireEvent('change', this, v, this.startValue);
11162         }
11163         
11164     },
11165     
11166     /**
11167      * Resets the current field value to the originally loaded value and clears any validation messages
11168      */
11169     reset : function(){
11170         this.setValue(this.originalValue);
11171         this.validate();
11172     },
11173      /**
11174      * Returns the name of the field
11175      * @return {Mixed} name The name field
11176      */
11177     getName: function(){
11178         return this.name;
11179     },
11180      /**
11181      * Returns the normalized data value (undefined or emptyText will be returned as '').  To return the raw value see {@link #getRawValue}.
11182      * @return {Mixed} value The field value
11183      */
11184     getValue : function(){
11185         
11186         var v = this.inputEl().getValue();
11187         
11188         return v;
11189     },
11190     /**
11191      * Returns the raw data value which may or may not be a valid, defined value.  To return a normalized value see {@link #getValue}.
11192      * @return {Mixed} value The field value
11193      */
11194     getRawValue : function(){
11195         var v = this.inputEl().getValue();
11196         
11197         return v;
11198     },
11199     
11200     /**
11201      * Sets the underlying DOM field's value directly, bypassing validation.  To set the value with validation see {@link #setValue}.
11202      * @param {Mixed} value The value to set
11203      */
11204     setRawValue : function(v){
11205         return this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11206     },
11207     
11208     selectText : function(start, end){
11209         var v = this.getRawValue();
11210         if(v.length > 0){
11211             start = start === undefined ? 0 : start;
11212             end = end === undefined ? v.length : end;
11213             var d = this.inputEl().dom;
11214             if(d.setSelectionRange){
11215                 d.setSelectionRange(start, end);
11216             }else if(d.createTextRange){
11217                 var range = d.createTextRange();
11218                 range.moveStart("character", start);
11219                 range.moveEnd("character", v.length-end);
11220                 range.select();
11221             }
11222         }
11223     },
11224     
11225     /**
11226      * Sets a data value into the field and validates it.  To set the value directly without validation see {@link #setRawValue}.
11227      * @param {Mixed} value The value to set
11228      */
11229     setValue : function(v){
11230         this.value = v;
11231         if(this.rendered){
11232             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
11233             this.validate();
11234         }
11235     },
11236     
11237     /*
11238     processValue : function(value){
11239         if(this.stripCharsRe){
11240             var newValue = value.replace(this.stripCharsRe, '');
11241             if(newValue !== value){
11242                 this.setRawValue(newValue);
11243                 return newValue;
11244             }
11245         }
11246         return value;
11247     },
11248   */
11249     preFocus : function(){
11250         
11251         if(this.selectOnFocus){
11252             this.inputEl().dom.select();
11253         }
11254     },
11255     filterKeys : function(e){
11256         var k = e.getKey();
11257         if(!Roo.isIE && (e.isNavKeyPress() || k == e.BACKSPACE || (k == e.DELETE && e.button == -1))){
11258             return;
11259         }
11260         var c = e.getCharCode(), cc = String.fromCharCode(c);
11261         if(Roo.isIE && (e.isSpecialKey() || !cc)){
11262             return;
11263         }
11264         if(!this.maskRe.test(cc)){
11265             e.stopEvent();
11266         }
11267     },
11268      /**
11269      * Clear any invalid styles/messages for this field
11270      */
11271     clearInvalid : function(){
11272         
11273         if(!this.el || this.preventMark){ // not rendered
11274             return;
11275         }
11276         
11277         
11278         this.el.removeClass([this.invalidClass, 'is-invalid']);
11279         
11280         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11281             
11282             var feedback = this.el.select('.form-control-feedback', true).first();
11283             
11284             if(feedback){
11285                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11286             }
11287             
11288         }
11289         
11290         if(this.indicator){
11291             this.indicator.removeClass('visible');
11292             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11293         }
11294         
11295         this.fireEvent('valid', this);
11296     },
11297     
11298      /**
11299      * Mark this field as valid
11300      */
11301     markValid : function()
11302     {
11303         if(!this.el  || this.preventMark){ // not rendered...
11304             return;
11305         }
11306         
11307         this.el.removeClass([this.invalidClass, this.validClass]);
11308         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11309
11310         var feedback = this.el.select('.form-control-feedback', true).first();
11311             
11312         if(feedback){
11313             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11314         }
11315         
11316         if(this.indicator){
11317             this.indicator.removeClass('visible');
11318             this.indicator.addClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11319         }
11320         
11321         if(this.disabled){
11322             return;
11323         }
11324         
11325            
11326         if(this.allowBlank && !this.getRawValue().length){
11327             return;
11328         }
11329         if (Roo.bootstrap.version == 3) {
11330             this.el.addClass(this.validClass);
11331         } else {
11332             this.inputEl().addClass('is-valid');
11333         }
11334
11335         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11336             
11337             var feedback = this.el.select('.form-control-feedback', true).first();
11338             
11339             if(feedback){
11340                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11341                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11342             }
11343             
11344         }
11345         
11346         this.fireEvent('valid', this);
11347     },
11348     
11349      /**
11350      * Mark this field as invalid
11351      * @param {String} msg The validation message
11352      */
11353     markInvalid : function(msg)
11354     {
11355         if(!this.el  || this.preventMark){ // not rendered
11356             return;
11357         }
11358         
11359         this.el.removeClass([this.invalidClass, this.validClass]);
11360         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11361         
11362         var feedback = this.el.select('.form-control-feedback', true).first();
11363             
11364         if(feedback){
11365             this.el.select('.form-control-feedback', true).first().removeClass(
11366                     [this.invalidFeedbackClass, this.validFeedbackClass]);
11367         }
11368
11369         if(this.disabled){
11370             return;
11371         }
11372         
11373         if(this.allowBlank && !this.getRawValue().length){
11374             return;
11375         }
11376         
11377         if(this.indicator){
11378             this.indicator.removeClass(this.indicatorpos == 'right' ? 'hidden' : 'invisible');
11379             this.indicator.addClass('visible');
11380         }
11381         if (Roo.bootstrap.version == 3) {
11382             this.el.addClass(this.invalidClass);
11383         } else {
11384             this.inputEl().addClass('is-invalid');
11385         }
11386         
11387         
11388         
11389         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11390             
11391             var feedback = this.el.select('.form-control-feedback', true).first();
11392             
11393             if(feedback){
11394                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11395                 
11396                 if(this.getValue().length || this.forceFeedback){
11397                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11398                 }
11399                 
11400             }
11401             
11402         }
11403         
11404         this.fireEvent('invalid', this, msg);
11405     },
11406     // private
11407     SafariOnKeyDown : function(event)
11408     {
11409         // this is a workaround for a password hang bug on chrome/ webkit.
11410         if (this.inputEl().dom.type != 'password') {
11411             return;
11412         }
11413         
11414         var isSelectAll = false;
11415         
11416         if(this.inputEl().dom.selectionEnd > 0){
11417             isSelectAll = (this.inputEl().dom.selectionEnd - this.inputEl().dom.selectionStart - this.getValue().length == 0) ? true : false;
11418         }
11419         if(((event.getKey() == 8 || event.getKey() == 46) && this.getValue().length ==1)){ // backspace and delete key
11420             event.preventDefault();
11421             this.setValue('');
11422             return;
11423         }
11424         
11425         if(isSelectAll  && event.getCharCode() > 31 && !event.ctrlKey) { // not backspace and delete key (or ctrl-v)
11426             
11427             event.preventDefault();
11428             // this is very hacky as keydown always get's upper case.
11429             //
11430             var cc = String.fromCharCode(event.getCharCode());
11431             this.setValue( event.shiftKey ?  cc : cc.toLowerCase());
11432             
11433         }
11434     },
11435     adjustWidth : function(tag, w){
11436         tag = tag.toLowerCase();
11437         if(typeof w == 'number' && Roo.isStrict && !Roo.isSafari){
11438             if(Roo.isIE && (tag == 'input' || tag == 'textarea')){
11439                 if(tag == 'input'){
11440                     return w + 2;
11441                 }
11442                 if(tag == 'textarea'){
11443                     return w-2;
11444                 }
11445             }else if(Roo.isOpera){
11446                 if(tag == 'input'){
11447                     return w + 2;
11448                 }
11449                 if(tag == 'textarea'){
11450                     return w-2;
11451                 }
11452             }
11453         }
11454         return w;
11455     },
11456     
11457     setFieldLabel : function(v)
11458     {
11459         if(!this.rendered){
11460             return;
11461         }
11462         
11463         if(this.indicatorEl()){
11464             var ar = this.el.select('label > span',true);
11465             
11466             if (ar.elements.length) {
11467                 this.el.select('label > span',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11468                 this.fieldLabel = v;
11469                 return;
11470             }
11471             
11472             var br = this.el.select('label',true);
11473             
11474             if(br.elements.length) {
11475                 this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11476                 this.fieldLabel = v;
11477                 return;
11478             }
11479             
11480             Roo.log('Cannot Found any of label > span || label in input');
11481             return;
11482         }
11483         
11484         this.el.select('label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
11485         this.fieldLabel = v;
11486         
11487         
11488     }
11489 });
11490
11491  
11492 /*
11493  * - LGPL
11494  *
11495  * Input
11496  * 
11497  */
11498
11499 /**
11500  * @class Roo.bootstrap.TextArea
11501  * @extends Roo.bootstrap.Input
11502  * Bootstrap TextArea class
11503  * @cfg {Number} cols Specifies the visible width of a text area
11504  * @cfg {Number} rows Specifies the visible number of lines in a text area
11505  * @cfg {string} wrap (soft|hard)Specifies how the text in a text area is to be wrapped when submitted in a form
11506  * @cfg {string} resize (none|both|horizontal|vertical|inherit|initial)
11507  * @cfg {string} html text
11508  * 
11509  * @constructor
11510  * Create a new TextArea
11511  * @param {Object} config The config object
11512  */
11513
11514 Roo.bootstrap.TextArea = function(config){
11515     Roo.bootstrap.TextArea.superclass.constructor.call(this, config);
11516    
11517 };
11518
11519 Roo.extend(Roo.bootstrap.TextArea, Roo.bootstrap.Input,  {
11520      
11521     cols : false,
11522     rows : 5,
11523     readOnly : false,
11524     warp : 'soft',
11525     resize : false,
11526     value: false,
11527     html: false,
11528     
11529     getAutoCreate : function(){
11530         
11531         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
11532         
11533         var id = Roo.id();
11534         
11535         var cfg = {};
11536         
11537         if(this.inputType != 'hidden'){
11538             cfg.cls = 'form-group' //input-group
11539         }
11540         
11541         var input =  {
11542             tag: 'textarea',
11543             id : id,
11544             warp : this.warp,
11545             rows : this.rows,
11546             value : this.value || '',
11547             html: this.html || '',
11548             cls : 'form-control',
11549             placeholder : this.placeholder || '' 
11550             
11551         };
11552         
11553         if(this.maxLength && this.maxLength != Number.MAX_VALUE){
11554             input.maxLength = this.maxLength;
11555         }
11556         
11557         if(this.resize){
11558             input.style = (typeof(input.style) == 'undefined') ? 'resize:' + this.resize : input.style + 'resize:' + this.resize;
11559         }
11560         
11561         if(this.cols){
11562             input.cols = this.cols;
11563         }
11564         
11565         if (this.readOnly) {
11566             input.readonly = true;
11567         }
11568         
11569         if (this.name) {
11570             input.name = this.name;
11571         }
11572         
11573         if (this.size) {
11574             input.cls = (typeof(input.cls) == 'undefined') ? 'input-' + this.size : input.cls + ' input-' + this.size;
11575         }
11576         
11577         var settings=this;
11578         ['xs','sm','md','lg'].map(function(size){
11579             if (settings[size]) {
11580                 cfg.cls += ' col-' + size + '-' + settings[size];
11581             }
11582         });
11583         
11584         var inputblock = input;
11585         
11586         if(this.hasFeedback && !this.allowBlank){
11587             
11588             var feedback = {
11589                 tag: 'span',
11590                 cls: 'glyphicon form-control-feedback'
11591             };
11592
11593             inputblock = {
11594                 cls : 'has-feedback',
11595                 cn :  [
11596                     input,
11597                     feedback
11598                 ] 
11599             };  
11600         }
11601         
11602         
11603         if (this.before || this.after) {
11604             
11605             inputblock = {
11606                 cls : 'input-group',
11607                 cn :  [] 
11608             };
11609             if (this.before) {
11610                 inputblock.cn.push({
11611                     tag :'span',
11612                     cls : 'input-group-addon',
11613                     html : this.before
11614                 });
11615             }
11616             
11617             inputblock.cn.push(input);
11618             
11619             if(this.hasFeedback && !this.allowBlank){
11620                 inputblock.cls += ' has-feedback';
11621                 inputblock.cn.push(feedback);
11622             }
11623             
11624             if (this.after) {
11625                 inputblock.cn.push({
11626                     tag :'span',
11627                     cls : 'input-group-addon',
11628                     html : this.after
11629                 });
11630             }
11631             
11632         }
11633         
11634         if (align ==='left' && this.fieldLabel.length) {
11635             cfg.cn = [
11636                 {
11637                     tag: 'label',
11638                     'for' :  id,
11639                     cls : 'control-label',
11640                     html : this.fieldLabel
11641                 },
11642                 {
11643                     cls : "",
11644                     cn: [
11645                         inputblock
11646                     ]
11647                 }
11648
11649             ];
11650             
11651             if(this.labelWidth > 12){
11652                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
11653             }
11654
11655             if(this.labelWidth < 13 && this.labelmd == 0){
11656                 this.labelmd = this.labelWidth;
11657             }
11658
11659             if(this.labellg > 0){
11660                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
11661                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
11662             }
11663
11664             if(this.labelmd > 0){
11665                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
11666                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
11667             }
11668
11669             if(this.labelsm > 0){
11670                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
11671                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
11672             }
11673
11674             if(this.labelxs > 0){
11675                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
11676                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
11677             }
11678             
11679         } else if ( this.fieldLabel.length) {
11680             cfg.cn = [
11681
11682                {
11683                    tag: 'label',
11684                    //cls : 'input-group-addon',
11685                    html : this.fieldLabel
11686
11687                },
11688
11689                inputblock
11690
11691            ];
11692
11693         } else {
11694
11695             cfg.cn = [
11696
11697                 inputblock
11698
11699             ];
11700                 
11701         }
11702         
11703         if (this.disabled) {
11704             input.disabled=true;
11705         }
11706         
11707         return cfg;
11708         
11709     },
11710     /**
11711      * return the real textarea element.
11712      */
11713     inputEl: function ()
11714     {
11715         return this.el.select('textarea.form-control',true).first();
11716     },
11717     
11718     /**
11719      * Clear any invalid styles/messages for this field
11720      */
11721     clearInvalid : function()
11722     {
11723         
11724         if(!this.el || this.preventMark){ // not rendered
11725             return;
11726         }
11727         
11728         var label = this.el.select('label', true).first();
11729         var icon = this.el.select('i.fa-star', true).first();
11730         
11731         if(label && icon){
11732             icon.remove();
11733         }
11734         this.el.removeClass( this.validClass);
11735         this.inputEl().removeClass('is-invalid');
11736          
11737         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11738             
11739             var feedback = this.el.select('.form-control-feedback', true).first();
11740             
11741             if(feedback){
11742                 this.el.select('.form-control-feedback', true).first().removeClass(this.invalidFeedbackClass);
11743             }
11744             
11745         }
11746         
11747         this.fireEvent('valid', this);
11748     },
11749     
11750      /**
11751      * Mark this field as valid
11752      */
11753     markValid : function()
11754     {
11755         if(!this.el  || this.preventMark){ // not rendered
11756             return;
11757         }
11758         
11759         this.el.removeClass([this.invalidClass, this.validClass]);
11760         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11761         
11762         var feedback = this.el.select('.form-control-feedback', true).first();
11763             
11764         if(feedback){
11765             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11766         }
11767
11768         if(this.disabled || this.allowBlank){
11769             return;
11770         }
11771         
11772         var label = this.el.select('label', true).first();
11773         var icon = this.el.select('i.fa-star', true).first();
11774         
11775         if(label && icon){
11776             icon.remove();
11777         }
11778         if (Roo.bootstrap.version == 3) {
11779             this.el.addClass(this.validClass);
11780         } else {
11781             this.inputEl().addClass('is-valid');
11782         }
11783         
11784         
11785         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank && (this.getValue().length || this.forceFeedback)){
11786             
11787             var feedback = this.el.select('.form-control-feedback', true).first();
11788             
11789             if(feedback){
11790                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11791                 this.el.select('.form-control-feedback', true).first().addClass([this.validFeedbackClass]);
11792             }
11793             
11794         }
11795         
11796         this.fireEvent('valid', this);
11797     },
11798     
11799      /**
11800      * Mark this field as invalid
11801      * @param {String} msg The validation message
11802      */
11803     markInvalid : function(msg)
11804     {
11805         if(!this.el  || this.preventMark){ // not rendered
11806             return;
11807         }
11808         
11809         this.el.removeClass([this.invalidClass, this.validClass]);
11810         this.inputEl().removeClass(['is-valid', 'is-invalid']);
11811         
11812         var feedback = this.el.select('.form-control-feedback', true).first();
11813             
11814         if(feedback){
11815             this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11816         }
11817
11818         if(this.disabled || this.allowBlank){
11819             return;
11820         }
11821         
11822         var label = this.el.select('label', true).first();
11823         var icon = this.el.select('i.fa-star', true).first();
11824         
11825         if(!this.getValue().length && label && !icon){
11826             this.el.createChild({
11827                 tag : 'i',
11828                 cls : 'text-danger fa fa-lg fa-star',
11829                 tooltip : 'This field is required',
11830                 style : 'margin-right:5px;'
11831             }, label, true);
11832         }
11833         
11834         if (Roo.bootstrap.version == 3) {
11835             this.el.addClass(this.invalidClass);
11836         } else {
11837             this.inputEl().addClass('is-invalid');
11838         }
11839         
11840         // fixme ... this may be depricated need to test..
11841         if(this.hasFeedback && this.inputType != 'hidden' && !this.allowBlank){
11842             
11843             var feedback = this.el.select('.form-control-feedback', true).first();
11844             
11845             if(feedback){
11846                 this.el.select('.form-control-feedback', true).first().removeClass([this.invalidFeedbackClass, this.validFeedbackClass]);
11847                 
11848                 if(this.getValue().length || this.forceFeedback){
11849                     this.el.select('.form-control-feedback', true).first().addClass([this.invalidFeedbackClass]);
11850                 }
11851                 
11852             }
11853             
11854         }
11855         
11856         this.fireEvent('invalid', this, msg);
11857     }
11858 });
11859
11860  
11861 /*
11862  * - LGPL
11863  *
11864  * trigger field - base class for combo..
11865  * 
11866  */
11867  
11868 /**
11869  * @class Roo.bootstrap.TriggerField
11870  * @extends Roo.bootstrap.Input
11871  * Provides a convenient wrapper for TextFields that adds a clickable trigger button (looks like a combobox by default).
11872  * The trigger has no default action, so you must assign a function to implement the trigger click handler by
11873  * overriding {@link #onTriggerClick}. You can create a TriggerField directly, as it renders exactly like a combobox
11874  * for which you can provide a custom implementation.  For example:
11875  * <pre><code>
11876 var trigger = new Roo.bootstrap.TriggerField();
11877 trigger.onTriggerClick = myTriggerFn;
11878 trigger.applyTo('my-field');
11879 </code></pre>
11880  *
11881  * However, in general you will most likely want to use TriggerField as the base class for a reusable component.
11882  * {@link Roo.bootstrap.DateField} and {@link Roo.bootstrap.ComboBox} are perfect examples of this.
11883  * @cfg {String} triggerClass An additional CSS class used to style the trigger button.  The trigger will always get the
11884  * class 'x-form-trigger' by default and triggerClass will be <b>appended</b> if specified.
11885  * @cfg {String} caret (search|calendar) BS3 only - carat fa name
11886
11887  * @constructor
11888  * Create a new TriggerField.
11889  * @param {Object} config Configuration options (valid {@Roo.bootstrap.Input} config options will also be applied
11890  * to the base TextField)
11891  */
11892 Roo.bootstrap.TriggerField = function(config){
11893     this.mimicing = false;
11894     Roo.bootstrap.TriggerField.superclass.constructor.call(this, config);
11895 };
11896
11897 Roo.extend(Roo.bootstrap.TriggerField, Roo.bootstrap.Input,  {
11898     /**
11899      * @cfg {String} triggerClass A CSS class to apply to the trigger
11900      */
11901      /**
11902      * @cfg {Boolean} hideTrigger True to hide the trigger element and display only the base text field (defaults to false)
11903      */
11904     hideTrigger:false,
11905
11906     /**
11907      * @cfg {Boolean} removable (true|false) special filter default false
11908      */
11909     removable : false,
11910     
11911     /** @cfg {Boolean} grow @hide */
11912     /** @cfg {Number} growMin @hide */
11913     /** @cfg {Number} growMax @hide */
11914
11915     /**
11916      * @hide 
11917      * @method
11918      */
11919     autoSize: Roo.emptyFn,
11920     // private
11921     monitorTab : true,
11922     // private
11923     deferHeight : true,
11924
11925     
11926     actionMode : 'wrap',
11927     
11928     caret : false,
11929     
11930     
11931     getAutoCreate : function(){
11932        
11933         var align = this.labelAlign || this.parentLabelAlign();
11934         
11935         var id = Roo.id();
11936         
11937         var cfg = {
11938             cls: 'form-group' //input-group
11939         };
11940         
11941         
11942         var input =  {
11943             tag: 'input',
11944             id : id,
11945             type : this.inputType,
11946             cls : 'form-control',
11947             autocomplete: 'new-password',
11948             placeholder : this.placeholder || '' 
11949             
11950         };
11951         if (this.name) {
11952             input.name = this.name;
11953         }
11954         if (this.size) {
11955             input.cls += ' input-' + this.size;
11956         }
11957         
11958         if (this.disabled) {
11959             input.disabled=true;
11960         }
11961         
11962         var inputblock = input;
11963         
11964         if(this.hasFeedback && !this.allowBlank){
11965             
11966             var feedback = {
11967                 tag: 'span',
11968                 cls: 'glyphicon form-control-feedback'
11969             };
11970             
11971             if(this.removable && !this.editable  ){
11972                 inputblock = {
11973                     cls : 'has-feedback',
11974                     cn :  [
11975                         inputblock,
11976                         {
11977                             tag: 'button',
11978                             html : 'x',
11979                             cls : 'roo-combo-removable-btn close'
11980                         },
11981                         feedback
11982                     ] 
11983                 };
11984             } else {
11985                 inputblock = {
11986                     cls : 'has-feedback',
11987                     cn :  [
11988                         inputblock,
11989                         feedback
11990                     ] 
11991                 };
11992             }
11993
11994         } else {
11995             if(this.removable && !this.editable ){
11996                 inputblock = {
11997                     cls : 'roo-removable',
11998                     cn :  [
11999                         inputblock,
12000                         {
12001                             tag: 'button',
12002                             html : 'x',
12003                             cls : 'roo-combo-removable-btn close'
12004                         }
12005                     ] 
12006                 };
12007             }
12008         }
12009         
12010         if (this.before || this.after) {
12011             
12012             inputblock = {
12013                 cls : 'input-group',
12014                 cn :  [] 
12015             };
12016             if (this.before) {
12017                 inputblock.cn.push({
12018                     tag :'span',
12019                     cls : 'input-group-addon input-group-prepend input-group-text',
12020                     html : this.before
12021                 });
12022             }
12023             
12024             inputblock.cn.push(input);
12025             
12026             if(this.hasFeedback && !this.allowBlank){
12027                 inputblock.cls += ' has-feedback';
12028                 inputblock.cn.push(feedback);
12029             }
12030             
12031             if (this.after) {
12032                 inputblock.cn.push({
12033                     tag :'span',
12034                     cls : 'input-group-addon input-group-append input-group-text',
12035                     html : this.after
12036                 });
12037             }
12038             
12039         };
12040         
12041       
12042         
12043         var ibwrap = inputblock;
12044         
12045         if(this.multiple){
12046             ibwrap = {
12047                 tag: 'ul',
12048                 cls: 'roo-select2-choices',
12049                 cn:[
12050                     {
12051                         tag: 'li',
12052                         cls: 'roo-select2-search-field',
12053                         cn: [
12054
12055                             inputblock
12056                         ]
12057                     }
12058                 ]
12059             };
12060                 
12061         }
12062         
12063         var combobox = {
12064             cls: 'roo-select2-container input-group',
12065             cn: [
12066                  {
12067                     tag: 'input',
12068                     type : 'hidden',
12069                     cls: 'form-hidden-field'
12070                 },
12071                 ibwrap
12072             ]
12073         };
12074         
12075         if(!this.multiple && this.showToggleBtn){
12076             
12077             var caret = {
12078                         tag: 'span',
12079                         cls: 'caret'
12080              };
12081             if (this.caret != false) {
12082                 caret = {
12083                      tag: 'i',
12084                      cls: 'fa fa-' + this.caret
12085                 };
12086                 
12087             }
12088             
12089             combobox.cn.push({
12090                 tag :'span',
12091                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
12092                 cn : [
12093                     Roo.bootstrap.version == 3 ? caret : '',
12094                     {
12095                         tag: 'span',
12096                         cls: 'combobox-clear',
12097                         cn  : [
12098                             {
12099                                 tag : 'i',
12100                                 cls: 'icon-remove'
12101                             }
12102                         ]
12103                     }
12104                 ]
12105
12106             })
12107         }
12108         
12109         if(this.multiple){
12110             combobox.cls += ' roo-select2-container-multi';
12111         }
12112          var indicator = {
12113             tag : 'i',
12114             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
12115             tooltip : 'This field is required'
12116         };
12117         if (Roo.bootstrap.version == 4) {
12118             indicator = {
12119                 tag : 'i',
12120                 style : 'display:none'
12121             };
12122         }
12123         
12124         
12125         if (align ==='left' && this.fieldLabel.length) {
12126             
12127             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
12128
12129             cfg.cn = [
12130                 indicator,
12131                 {
12132                     tag: 'label',
12133                     'for' :  id,
12134                     cls : 'control-label',
12135                     html : this.fieldLabel
12136
12137                 },
12138                 {
12139                     cls : "", 
12140                     cn: [
12141                         combobox
12142                     ]
12143                 }
12144
12145             ];
12146             
12147             var labelCfg = cfg.cn[1];
12148             var contentCfg = cfg.cn[2];
12149             
12150             if(this.indicatorpos == 'right'){
12151                 cfg.cn = [
12152                     {
12153                         tag: 'label',
12154                         'for' :  id,
12155                         cls : 'control-label',
12156                         cn : [
12157                             {
12158                                 tag : 'span',
12159                                 html : this.fieldLabel
12160                             },
12161                             indicator
12162                         ]
12163                     },
12164                     {
12165                         cls : "", 
12166                         cn: [
12167                             combobox
12168                         ]
12169                     }
12170
12171                 ];
12172                 
12173                 labelCfg = cfg.cn[0];
12174                 contentCfg = cfg.cn[1];
12175             }
12176             
12177             if(this.labelWidth > 12){
12178                 labelCfg.style = "width: " + this.labelWidth + 'px';
12179             }
12180             
12181             if(this.labelWidth < 13 && this.labelmd == 0){
12182                 this.labelmd = this.labelWidth;
12183             }
12184             
12185             if(this.labellg > 0){
12186                 labelCfg.cls += ' col-lg-' + this.labellg;
12187                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
12188             }
12189             
12190             if(this.labelmd > 0){
12191                 labelCfg.cls += ' col-md-' + this.labelmd;
12192                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
12193             }
12194             
12195             if(this.labelsm > 0){
12196                 labelCfg.cls += ' col-sm-' + this.labelsm;
12197                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
12198             }
12199             
12200             if(this.labelxs > 0){
12201                 labelCfg.cls += ' col-xs-' + this.labelxs;
12202                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
12203             }
12204             
12205         } else if ( this.fieldLabel.length) {
12206 //                Roo.log(" label");
12207             cfg.cn = [
12208                 indicator,
12209                {
12210                    tag: 'label',
12211                    //cls : 'input-group-addon',
12212                    html : this.fieldLabel
12213
12214                },
12215
12216                combobox
12217
12218             ];
12219             
12220             if(this.indicatorpos == 'right'){
12221                 
12222                 cfg.cn = [
12223                     {
12224                        tag: 'label',
12225                        cn : [
12226                            {
12227                                tag : 'span',
12228                                html : this.fieldLabel
12229                            },
12230                            indicator
12231                        ]
12232
12233                     },
12234                     combobox
12235
12236                 ];
12237
12238             }
12239
12240         } else {
12241             
12242 //                Roo.log(" no label && no align");
12243                 cfg = combobox
12244                      
12245                 
12246         }
12247         
12248         var settings=this;
12249         ['xs','sm','md','lg'].map(function(size){
12250             if (settings[size]) {
12251                 cfg.cls += ' col-' + size + '-' + settings[size];
12252             }
12253         });
12254         
12255         return cfg;
12256         
12257     },
12258     
12259     
12260     
12261     // private
12262     onResize : function(w, h){
12263 //        Roo.bootstrap.TriggerField.superclass.onResize.apply(this, arguments);
12264 //        if(typeof w == 'number'){
12265 //            var x = w - this.trigger.getWidth();
12266 //            this.inputEl().setWidth(this.adjustWidth('input', x));
12267 //            this.trigger.setStyle('left', x+'px');
12268 //        }
12269     },
12270
12271     // private
12272     adjustSize : Roo.BoxComponent.prototype.adjustSize,
12273
12274     // private
12275     getResizeEl : function(){
12276         return this.inputEl();
12277     },
12278
12279     // private
12280     getPositionEl : function(){
12281         return this.inputEl();
12282     },
12283
12284     // private
12285     alignErrorIcon : function(){
12286         this.errorIcon.alignTo(this.inputEl(), 'tl-tr', [2, 0]);
12287     },
12288
12289     // private
12290     initEvents : function(){
12291         
12292         this.createList();
12293         
12294         Roo.bootstrap.TriggerField.superclass.initEvents.call(this);
12295         //this.wrap = this.el.wrap({cls: "x-form-field-wrap"});
12296         if(!this.multiple && this.showToggleBtn){
12297             this.trigger = this.el.select('span.dropdown-toggle',true).first();
12298             if(this.hideTrigger){
12299                 this.trigger.setDisplayed(false);
12300             }
12301             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
12302         }
12303         
12304         if(this.multiple){
12305             this.inputEl().on("click", this.onTriggerClick, this, {preventDefault:true});
12306         }
12307         
12308         if(this.removable && !this.editable && !this.tickable){
12309             var close = this.closeTriggerEl();
12310             
12311             if(close){
12312                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
12313                 close.on('click', this.removeBtnClick, this, close);
12314             }
12315         }
12316         
12317         //this.trigger.addClassOnOver('x-form-trigger-over');
12318         //this.trigger.addClassOnClick('x-form-trigger-click');
12319         
12320         //if(!this.width){
12321         //    this.wrap.setWidth(this.el.getWidth()+this.trigger.getWidth());
12322         //}
12323     },
12324     
12325     closeTriggerEl : function()
12326     {
12327         var close = this.el.select('.roo-combo-removable-btn', true).first();
12328         return close ? close : false;
12329     },
12330     
12331     removeBtnClick : function(e, h, el)
12332     {
12333         e.preventDefault();
12334         
12335         if(this.fireEvent("remove", this) !== false){
12336             this.reset();
12337             this.fireEvent("afterremove", this)
12338         }
12339     },
12340     
12341     createList : function()
12342     {
12343         this.list = Roo.get(document.body).createChild({
12344             tag: Roo.bootstrap.version == 4 ? 'div' : 'ul',
12345             cls: 'typeahead typeahead-long dropdown-menu shadow',
12346             style: 'display:none'
12347         });
12348         
12349         this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';;
12350         
12351     },
12352
12353     // private
12354     initTrigger : function(){
12355        
12356     },
12357
12358     // private
12359     onDestroy : function(){
12360         if(this.trigger){
12361             this.trigger.removeAllListeners();
12362           //  this.trigger.remove();
12363         }
12364         //if(this.wrap){
12365         //    this.wrap.remove();
12366         //}
12367         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
12368     },
12369
12370     // private
12371     onFocus : function(){
12372         Roo.bootstrap.TriggerField.superclass.onFocus.call(this);
12373         /*
12374         if(!this.mimicing){
12375             this.wrap.addClass('x-trigger-wrap-focus');
12376             this.mimicing = true;
12377             Roo.get(Roo.isIE ? document.body : document).on("mousedown", this.mimicBlur, this);
12378             if(this.monitorTab){
12379                 this.el.on("keydown", this.checkTab, this);
12380             }
12381         }
12382         */
12383     },
12384
12385     // private
12386     checkTab : function(e){
12387         if(e.getKey() == e.TAB){
12388             this.triggerBlur();
12389         }
12390     },
12391
12392     // private
12393     onBlur : function(){
12394         // do nothing
12395     },
12396
12397     // private
12398     mimicBlur : function(e, t){
12399         /*
12400         if(!this.wrap.contains(t) && this.validateBlur()){
12401             this.triggerBlur();
12402         }
12403         */
12404     },
12405
12406     // private
12407     triggerBlur : function(){
12408         this.mimicing = false;
12409         Roo.get(Roo.isIE ? document.body : document).un("mousedown", this.mimicBlur);
12410         if(this.monitorTab){
12411             this.el.un("keydown", this.checkTab, this);
12412         }
12413         //this.wrap.removeClass('x-trigger-wrap-focus');
12414         Roo.bootstrap.TriggerField.superclass.onBlur.call(this);
12415     },
12416
12417     // private
12418     // This should be overriden by any subclass that needs to check whether or not the field can be blurred.
12419     validateBlur : function(e, t){
12420         return true;
12421     },
12422
12423     // private
12424     onDisable : function(){
12425         this.inputEl().dom.disabled = true;
12426         //Roo.bootstrap.TriggerField.superclass.onDisable.call(this);
12427         //if(this.wrap){
12428         //    this.wrap.addClass('x-item-disabled');
12429         //}
12430     },
12431
12432     // private
12433     onEnable : function(){
12434         this.inputEl().dom.disabled = false;
12435         //Roo.bootstrap.TriggerField.superclass.onEnable.call(this);
12436         //if(this.wrap){
12437         //    this.el.removeClass('x-item-disabled');
12438         //}
12439     },
12440
12441     // private
12442     onShow : function(){
12443         var ae = this.getActionEl();
12444         
12445         if(ae){
12446             ae.dom.style.display = '';
12447             ae.dom.style.visibility = 'visible';
12448         }
12449     },
12450
12451     // private
12452     
12453     onHide : function(){
12454         var ae = this.getActionEl();
12455         ae.dom.style.display = 'none';
12456     },
12457
12458     /**
12459      * The function that should handle the trigger's click event.  This method does nothing by default until overridden
12460      * by an implementing function.
12461      * @method
12462      * @param {EventObject} e
12463      */
12464     onTriggerClick : Roo.emptyFn
12465 });
12466  
12467 /*
12468 * Licence: LGPL
12469 */
12470
12471 /**
12472  * @class Roo.bootstrap.CardUploader
12473  * @extends Roo.bootstrap.Button
12474  * Bootstrap Card Uploader class - it's a button which when you add files to it, adds cards below with preview and the name...
12475  * @cfg {Number} errorTimeout default 3000
12476  * @cfg {Array}  images  an array of ?? Img objects ??? when loading existing files..
12477  * @cfg {Array}  html The button text.
12478
12479  *
12480  * @constructor
12481  * Create a new CardUploader
12482  * @param {Object} config The config object
12483  */
12484
12485 Roo.bootstrap.CardUploader = function(config){
12486     
12487  
12488     
12489     Roo.bootstrap.CardUploader.superclass.constructor.call(this, config);
12490     
12491     
12492     this.fileCollection   = new Roo.util.MixedCollection(false,function(r) {
12493         return r.data.id
12494         });
12495     
12496     
12497 };
12498
12499 Roo.extend(Roo.bootstrap.CardUploader, Roo.bootstrap.Input,  {
12500     
12501      
12502     errorTimeout : 3000,
12503      
12504     images : false,
12505    
12506     fileCollection : false,
12507     allowBlank : true,
12508     
12509     getAutoCreate : function()
12510     {
12511         
12512         var cfg =  {
12513             cls :'form-group' ,
12514             cn : [
12515                
12516                 {
12517                     tag: 'label',
12518                    //cls : 'input-group-addon',
12519                     html : this.fieldLabel
12520
12521                 },
12522
12523                 {
12524                     tag: 'input',
12525                     type : 'hidden',
12526                     value : this.value,
12527                     cls : 'd-none  form-control'
12528                 },
12529                 
12530                 {
12531                     tag: 'input',
12532                     multiple : 'multiple',
12533                     type : 'file',
12534                     cls : 'd-none  roo-card-upload-selector'
12535                 },
12536                 
12537                 {
12538                     cls : 'roo-card-uploader-button-container w-100 mb-2'
12539                 },
12540                 {
12541                     cls : 'card-columns roo-card-uploader-container'
12542                 }
12543
12544             ]
12545         };
12546            
12547          
12548         return cfg;
12549     },
12550     
12551     getChildContainer : function() /// what children are added to.
12552     {
12553         return this.containerEl;
12554     },
12555    
12556     getButtonContainer : function() /// what children are added to.
12557     {
12558         return this.el.select(".roo-card-uploader-button-container").first();
12559     },
12560    
12561     initEvents : function()
12562     {
12563         
12564         Roo.bootstrap.Input.prototype.initEvents.call(this);
12565         
12566         var t = this;
12567         this.addxtype({
12568             xns: Roo.bootstrap,
12569
12570             xtype : 'Button',
12571             container_method : 'getButtonContainer' ,            
12572             html :  this.html, // fix changable?
12573             cls : 'w-100 ',
12574             listeners : {
12575                 'click' : function(btn, e) {
12576                     t.onClick(e);
12577                 }
12578             }
12579         });
12580         
12581         
12582         
12583         
12584         this.urlAPI = (window.createObjectURL && window) || 
12585                                 (window.URL && URL.revokeObjectURL && URL) || 
12586                                 (window.webkitURL && webkitURL);
12587                         
12588          
12589          
12590          
12591         this.selectorEl = this.el.select('.roo-card-upload-selector', true).first();
12592         
12593         this.selectorEl.on('change', this.onFileSelected, this);
12594         if (this.images) {
12595             var t = this;
12596             this.images.forEach(function(img) {
12597                 t.addCard(img)
12598             });
12599             this.images = false;
12600         }
12601         this.containerEl = this.el.select('.roo-card-uploader-container', true).first();
12602          
12603        
12604     },
12605     
12606    
12607     onClick : function(e)
12608     {
12609         e.preventDefault();
12610          
12611         this.selectorEl.dom.click();
12612          
12613     },
12614     
12615     onFileSelected : function(e)
12616     {
12617         e.preventDefault();
12618         
12619         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
12620             return;
12621         }
12622         
12623         Roo.each(this.selectorEl.dom.files, function(file){    
12624             this.addFile(file);
12625         }, this);
12626          
12627     },
12628     
12629       
12630     
12631       
12632     
12633     addFile : function(file)
12634     {
12635            
12636         if(typeof(file) === 'string'){
12637             throw "Add file by name?"; // should not happen
12638             return;
12639         }
12640         
12641         if(!file || !this.urlAPI){
12642             return;
12643         }
12644         
12645         // file;
12646         // file.type;
12647         
12648         var _this = this;
12649         
12650         
12651         var url = _this.urlAPI.createObjectURL( file);
12652            
12653         this.addCard({
12654             id : Roo.bootstrap.CardUploader.ID--,
12655             is_uploaded : false,
12656             src : url,
12657             title : file.name,
12658             mimetype : file.type,
12659             preview : false,
12660             is_deleted : 0
12661         })
12662         
12663     },
12664     
12665     addCard : function (data)
12666     {
12667         // hidden input element?
12668         // if the file is not an image...
12669         //then we need to use something other that and header_image
12670         var t = this;
12671         //   remove.....
12672         var footer = [
12673             {
12674                 xns : Roo.bootstrap,
12675                 xtype : 'CardFooter',
12676                 items: [
12677                     {
12678                         xns : Roo.bootstrap,
12679                         xtype : 'Element',
12680                         cls : 'd-flex',
12681                         items : [
12682                             
12683                             {
12684                                 xns : Roo.bootstrap,
12685                                 xtype : 'Button',
12686                                 html : String.format("<small>{0}</small>", data.title),
12687                                 cls : 'col-11 text-left',
12688                                 size: 'sm',
12689                                 weight: 'link',
12690                                 fa : 'download',
12691                                 listeners : {
12692                                     click : function() {
12693                                         this.downloadCard(data.id)
12694                                     }
12695                                 }
12696                             },
12697                           
12698                             {
12699                                 xns : Roo.bootstrap,
12700                                 xtype : 'Button',
12701                                 
12702                                 size : 'sm',
12703                                 weight: 'danger',
12704                                 cls : 'col-1',
12705                                 fa : 'times',
12706                                 listeners : {
12707                                     click : function() {
12708                                         t.removeCard(data.id)
12709                                     }
12710                                 }
12711                             }
12712                         ]
12713                     }
12714                     
12715                 ] 
12716             }
12717             
12718         ];
12719
12720         var cn = this.addxtype(
12721             {
12722                  
12723                 xns : Roo.bootstrap,
12724                 xtype : 'Card',
12725                 closeable : true,
12726                 header : !data.mimetype.match(/image/) && !data.preview ? "Document": false,
12727                 header_image : data.mimetype.match(/image/) ? data.src  : data.preview,
12728                 header_image_fit_square: true, // fixme  - we probably need to use the 'Img' element to do stuff like this.
12729                 data : data,
12730                 html : false,
12731                  
12732                 items : footer,
12733                 initEvents : function() {
12734                     Roo.bootstrap.Card.prototype.initEvents.call(this);
12735                     this.imgEl = this.el.select('.card-img-top').first();
12736                     if (this.imgEl) {
12737                         this.imgEl.on('click', function() { t.previewCard( data.id); }, this);
12738                         this.imgEl.set({ 'pointer' : 'cursor' });
12739                                   
12740                     }
12741                     
12742                   
12743                 }
12744                 
12745             }
12746         );
12747         // dont' really need ot update items.
12748         // this.items.push(cn);
12749         this.fileCollection.add(cn);
12750         this.updateInput();
12751         
12752     },
12753     removeCard : function(id)
12754     {
12755         
12756         var card  = this.fileCollection.get(id);
12757         card.data.is_deleted = 1;
12758         card.data.src = ''; /// delete the source - so it reduces size of not uploaded images etc.
12759         this.fileCollection.remove(card);
12760         //this.items = this.items.filter(function(e) { return e != card });
12761         // dont' really need ot update items.
12762         card.el.dom.parentNode.removeChild(card.el.dom);
12763         
12764     },
12765     reset: function()
12766     {
12767         this.fileCollection.each(function(card) {
12768             card.el.dom.parentNode.removeChild(card.el.dom);    
12769         });
12770         this.fileCollection.clear();
12771         this.updateInput();
12772     },
12773     
12774     updateInput : function()
12775     {
12776         var data = [];
12777         this.fileCollection.each(function(e) {
12778             data.push(e.data);
12779         });
12780         
12781         this.inputEl().dom.value = JSON.stringify(data);
12782     }
12783     
12784     
12785 });
12786
12787
12788 Roo.bootstrap.CardUploader.ID = -1;/*
12789  * Based on:
12790  * Ext JS Library 1.1.1
12791  * Copyright(c) 2006-2007, Ext JS, LLC.
12792  *
12793  * Originally Released Under LGPL - original licence link has changed is not relivant.
12794  *
12795  * Fork - LGPL
12796  * <script type="text/javascript">
12797  */
12798
12799
12800 /**
12801  * @class Roo.data.SortTypes
12802  * @singleton
12803  * Defines the default sorting (casting?) comparison functions used when sorting data.
12804  */
12805 Roo.data.SortTypes = {
12806     /**
12807      * Default sort that does nothing
12808      * @param {Mixed} s The value being converted
12809      * @return {Mixed} The comparison value
12810      */
12811     none : function(s){
12812         return s;
12813     },
12814     
12815     /**
12816      * The regular expression used to strip tags
12817      * @type {RegExp}
12818      * @property
12819      */
12820     stripTagsRE : /<\/?[^>]+>/gi,
12821     
12822     /**
12823      * Strips all HTML tags to sort on text only
12824      * @param {Mixed} s The value being converted
12825      * @return {String} The comparison value
12826      */
12827     asText : function(s){
12828         return String(s).replace(this.stripTagsRE, "");
12829     },
12830     
12831     /**
12832      * Strips all HTML tags to sort on text only - Case insensitive
12833      * @param {Mixed} s The value being converted
12834      * @return {String} The comparison value
12835      */
12836     asUCText : function(s){
12837         return String(s).toUpperCase().replace(this.stripTagsRE, "");
12838     },
12839     
12840     /**
12841      * Case insensitive string
12842      * @param {Mixed} s The value being converted
12843      * @return {String} The comparison value
12844      */
12845     asUCString : function(s) {
12846         return String(s).toUpperCase();
12847     },
12848     
12849     /**
12850      * Date sorting
12851      * @param {Mixed} s The value being converted
12852      * @return {Number} The comparison value
12853      */
12854     asDate : function(s) {
12855         if(!s){
12856             return 0;
12857         }
12858         if(s instanceof Date){
12859             return s.getTime();
12860         }
12861         return Date.parse(String(s));
12862     },
12863     
12864     /**
12865      * Float sorting
12866      * @param {Mixed} s The value being converted
12867      * @return {Float} The comparison value
12868      */
12869     asFloat : function(s) {
12870         var val = parseFloat(String(s).replace(/,/g, ""));
12871         if(isNaN(val)) {
12872             val = 0;
12873         }
12874         return val;
12875     },
12876     
12877     /**
12878      * Integer sorting
12879      * @param {Mixed} s The value being converted
12880      * @return {Number} The comparison value
12881      */
12882     asInt : function(s) {
12883         var val = parseInt(String(s).replace(/,/g, ""));
12884         if(isNaN(val)) {
12885             val = 0;
12886         }
12887         return val;
12888     }
12889 };/*
12890  * Based on:
12891  * Ext JS Library 1.1.1
12892  * Copyright(c) 2006-2007, Ext JS, LLC.
12893  *
12894  * Originally Released Under LGPL - original licence link has changed is not relivant.
12895  *
12896  * Fork - LGPL
12897  * <script type="text/javascript">
12898  */
12899
12900 /**
12901 * @class Roo.data.Record
12902  * Instances of this class encapsulate both record <em>definition</em> information, and record
12903  * <em>value</em> information for use in {@link Roo.data.Store} objects, or any code which needs
12904  * to access Records cached in an {@link Roo.data.Store} object.<br>
12905  * <p>
12906  * Constructors for this class are generated by passing an Array of field definition objects to {@link #create}.
12907  * Instances are usually only created by {@link Roo.data.Reader} implementations when processing unformatted data
12908  * objects.<br>
12909  * <p>
12910  * Record objects generated by this constructor inherit all the methods of Roo.data.Record listed below.
12911  * @constructor
12912  * This constructor should not be used to create Record objects. Instead, use the constructor generated by
12913  * {@link #create}. The parameters are the same.
12914  * @param {Array} data An associative Array of data values keyed by the field name.
12915  * @param {Object} id (Optional) The id of the record. This id should be unique, and is used by the
12916  * {@link Roo.data.Store} object which owns the Record to index its collection of Records. If
12917  * not specified an integer id is generated.
12918  */
12919 Roo.data.Record = function(data, id){
12920     this.id = (id || id === 0) ? id : ++Roo.data.Record.AUTO_ID;
12921     this.data = data;
12922 };
12923
12924 /**
12925  * Generate a constructor for a specific record layout.
12926  * @param {Array} o An Array of field definition objects which specify field names, and optionally,
12927  * data types, and a mapping for an {@link Roo.data.Reader} to extract the field's value from a data object.
12928  * Each field definition object may contain the following properties: <ul>
12929  * <li><b>name</b> : String<p style="margin-left:1em">The name by which the field is referenced within the Record. This is referenced by,
12930  * for example the <em>dataIndex</em> property in column definition objects passed to {@link Roo.grid.ColumnModel}</p></li>
12931  * <li><b>mapping</b> : String<p style="margin-left:1em">(Optional) A path specification for use by the {@link Roo.data.Reader} implementation
12932  * that is creating the Record to access the data value from the data object. If an {@link Roo.data.JsonReader}
12933  * is being used, then this is a string containing the javascript expression to reference the data relative to 
12934  * the record item's root. If an {@link Roo.data.XmlReader} is being used, this is an {@link Roo.DomQuery} path
12935  * to the data item relative to the record element. If the mapping expression is the same as the field name,
12936  * this may be omitted.</p></li>
12937  * <li><b>type</b> : String<p style="margin-left:1em">(Optional) The data type for conversion to displayable value. Possible values are
12938  * <ul><li>auto (Default, implies no conversion)</li>
12939  * <li>string</li>
12940  * <li>int</li>
12941  * <li>float</li>
12942  * <li>boolean</li>
12943  * <li>date</li></ul></p></li>
12944  * <li><b>sortType</b> : Mixed<p style="margin-left:1em">(Optional) A member of {@link Roo.data.SortTypes}.</p></li>
12945  * <li><b>sortDir</b> : String<p style="margin-left:1em">(Optional) Initial direction to sort. "ASC" or "DESC"</p></li>
12946  * <li><b>convert</b> : Function<p style="margin-left:1em">(Optional) A function which converts the value provided
12947  * by the Reader into an object that will be stored in the Record. It is passed the
12948  * following parameters:<ul>
12949  * <li><b>v</b> : Mixed<p style="margin-left:1em">The data value as read by the Reader.</p></li>
12950  * </ul></p></li>
12951  * <li><b>dateFormat</b> : String<p style="margin-left:1em">(Optional) A format String for the Date.parseDate function.</p></li>
12952  * </ul>
12953  * <br>usage:<br><pre><code>
12954 var TopicRecord = Roo.data.Record.create(
12955     {name: 'title', mapping: 'topic_title'},
12956     {name: 'author', mapping: 'username'},
12957     {name: 'totalPosts', mapping: 'topic_replies', type: 'int'},
12958     {name: 'lastPost', mapping: 'post_time', type: 'date'},
12959     {name: 'lastPoster', mapping: 'user2'},
12960     {name: 'excerpt', mapping: 'post_text'}
12961 );
12962
12963 var myNewRecord = new TopicRecord({
12964     title: 'Do my job please',
12965     author: 'noobie',
12966     totalPosts: 1,
12967     lastPost: new Date(),
12968     lastPoster: 'Animal',
12969     excerpt: 'No way dude!'
12970 });
12971 myStore.add(myNewRecord);
12972 </code></pre>
12973  * @method create
12974  * @static
12975  */
12976 Roo.data.Record.create = function(o){
12977     var f = function(){
12978         f.superclass.constructor.apply(this, arguments);
12979     };
12980     Roo.extend(f, Roo.data.Record);
12981     var p = f.prototype;
12982     p.fields = new Roo.util.MixedCollection(false, function(field){
12983         return field.name;
12984     });
12985     for(var i = 0, len = o.length; i < len; i++){
12986         p.fields.add(new Roo.data.Field(o[i]));
12987     }
12988     f.getField = function(name){
12989         return p.fields.get(name);  
12990     };
12991     return f;
12992 };
12993
12994 Roo.data.Record.AUTO_ID = 1000;
12995 Roo.data.Record.EDIT = 'edit';
12996 Roo.data.Record.REJECT = 'reject';
12997 Roo.data.Record.COMMIT = 'commit';
12998
12999 Roo.data.Record.prototype = {
13000     /**
13001      * Readonly flag - true if this record has been modified.
13002      * @type Boolean
13003      */
13004     dirty : false,
13005     editing : false,
13006     error: null,
13007     modified: null,
13008
13009     // private
13010     join : function(store){
13011         this.store = store;
13012     },
13013
13014     /**
13015      * Set the named field to the specified value.
13016      * @param {String} name The name of the field to set.
13017      * @param {Object} value The value to set the field to.
13018      */
13019     set : function(name, value){
13020         if(this.data[name] == value){
13021             return;
13022         }
13023         this.dirty = true;
13024         if(!this.modified){
13025             this.modified = {};
13026         }
13027         if(typeof this.modified[name] == 'undefined'){
13028             this.modified[name] = this.data[name];
13029         }
13030         this.data[name] = value;
13031         if(!this.editing && this.store){
13032             this.store.afterEdit(this);
13033         }       
13034     },
13035
13036     /**
13037      * Get the value of the named field.
13038      * @param {String} name The name of the field to get the value of.
13039      * @return {Object} The value of the field.
13040      */
13041     get : function(name){
13042         return this.data[name]; 
13043     },
13044
13045     // private
13046     beginEdit : function(){
13047         this.editing = true;
13048         this.modified = {}; 
13049     },
13050
13051     // private
13052     cancelEdit : function(){
13053         this.editing = false;
13054         delete this.modified;
13055     },
13056
13057     // private
13058     endEdit : function(){
13059         this.editing = false;
13060         if(this.dirty && this.store){
13061             this.store.afterEdit(this);
13062         }
13063     },
13064
13065     /**
13066      * Usually called by the {@link Roo.data.Store} which owns the Record.
13067      * Rejects all changes made to the Record since either creation, or the last commit operation.
13068      * Modified fields are reverted to their original values.
13069      * <p>
13070      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13071      * of reject operations.
13072      */
13073     reject : function(){
13074         var m = this.modified;
13075         for(var n in m){
13076             if(typeof m[n] != "function"){
13077                 this.data[n] = m[n];
13078             }
13079         }
13080         this.dirty = false;
13081         delete this.modified;
13082         this.editing = false;
13083         if(this.store){
13084             this.store.afterReject(this);
13085         }
13086     },
13087
13088     /**
13089      * Usually called by the {@link Roo.data.Store} which owns the Record.
13090      * Commits all changes made to the Record since either creation, or the last commit operation.
13091      * <p>
13092      * Developers should subscribe to the {@link Roo.data.Store#update} event to have their code notified
13093      * of commit operations.
13094      */
13095     commit : function(){
13096         this.dirty = false;
13097         delete this.modified;
13098         this.editing = false;
13099         if(this.store){
13100             this.store.afterCommit(this);
13101         }
13102     },
13103
13104     // private
13105     hasError : function(){
13106         return this.error != null;
13107     },
13108
13109     // private
13110     clearError : function(){
13111         this.error = null;
13112     },
13113
13114     /**
13115      * Creates a copy of this record.
13116      * @param {String} id (optional) A new record id if you don't want to use this record's id
13117      * @return {Record}
13118      */
13119     copy : function(newId) {
13120         return new this.constructor(Roo.apply({}, this.data), newId || this.id);
13121     }
13122 };/*
13123  * Based on:
13124  * Ext JS Library 1.1.1
13125  * Copyright(c) 2006-2007, Ext JS, LLC.
13126  *
13127  * Originally Released Under LGPL - original licence link has changed is not relivant.
13128  *
13129  * Fork - LGPL
13130  * <script type="text/javascript">
13131  */
13132
13133
13134
13135 /**
13136  * @class Roo.data.Store
13137  * @extends Roo.util.Observable
13138  * The Store class encapsulates a client side cache of {@link Roo.data.Record} objects which provide input data
13139  * for widgets such as the Roo.grid.Grid, or the Roo.form.ComboBox.<br>
13140  * <p>
13141  * A Store object uses an implementation of {@link Roo.data.DataProxy} to access a data object unless you call loadData() directly and pass in your data. The Store object
13142  * has no knowledge of the format of the data returned by the Proxy.<br>
13143  * <p>
13144  * A Store object uses its configured implementation of {@link Roo.data.DataReader} to create {@link Roo.data.Record}
13145  * instances from the data object. These records are cached and made available through accessor functions.
13146  * @constructor
13147  * Creates a new Store.
13148  * @param {Object} config A config object containing the objects needed for the Store to access data,
13149  * and read the data into Records.
13150  */
13151 Roo.data.Store = function(config){
13152     this.data = new Roo.util.MixedCollection(false);
13153     this.data.getKey = function(o){
13154         return o.id;
13155     };
13156     this.baseParams = {};
13157     // private
13158     this.paramNames = {
13159         "start" : "start",
13160         "limit" : "limit",
13161         "sort" : "sort",
13162         "dir" : "dir",
13163         "multisort" : "_multisort"
13164     };
13165
13166     if(config && config.data){
13167         this.inlineData = config.data;
13168         delete config.data;
13169     }
13170
13171     Roo.apply(this, config);
13172     
13173     if(this.reader){ // reader passed
13174         this.reader = Roo.factory(this.reader, Roo.data);
13175         this.reader.xmodule = this.xmodule || false;
13176         if(!this.recordType){
13177             this.recordType = this.reader.recordType;
13178         }
13179         if(this.reader.onMetaChange){
13180             this.reader.onMetaChange = this.onMetaChange.createDelegate(this);
13181         }
13182     }
13183
13184     if(this.recordType){
13185         this.fields = this.recordType.prototype.fields;
13186     }
13187     this.modified = [];
13188
13189     this.addEvents({
13190         /**
13191          * @event datachanged
13192          * Fires when the data cache has changed, and a widget which is using this Store
13193          * as a Record cache should refresh its view.
13194          * @param {Store} this
13195          */
13196         datachanged : true,
13197         /**
13198          * @event metachange
13199          * Fires when this store's reader provides new metadata (fields). This is currently only support for JsonReaders.
13200          * @param {Store} this
13201          * @param {Object} meta The JSON metadata
13202          */
13203         metachange : true,
13204         /**
13205          * @event add
13206          * Fires when Records have been added to the Store
13207          * @param {Store} this
13208          * @param {Roo.data.Record[]} records The array of Records added
13209          * @param {Number} index The index at which the record(s) were added
13210          */
13211         add : true,
13212         /**
13213          * @event remove
13214          * Fires when a Record has been removed from the Store
13215          * @param {Store} this
13216          * @param {Roo.data.Record} record The Record that was removed
13217          * @param {Number} index The index at which the record was removed
13218          */
13219         remove : true,
13220         /**
13221          * @event update
13222          * Fires when a Record has been updated
13223          * @param {Store} this
13224          * @param {Roo.data.Record} record The Record that was updated
13225          * @param {String} operation The update operation being performed.  Value may be one of:
13226          * <pre><code>
13227  Roo.data.Record.EDIT
13228  Roo.data.Record.REJECT
13229  Roo.data.Record.COMMIT
13230          * </code></pre>
13231          */
13232         update : true,
13233         /**
13234          * @event clear
13235          * Fires when the data cache has been cleared.
13236          * @param {Store} this
13237          */
13238         clear : true,
13239         /**
13240          * @event beforeload
13241          * Fires before a request is made for a new data object.  If the beforeload handler returns false
13242          * the load action will be canceled.
13243          * @param {Store} this
13244          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13245          */
13246         beforeload : true,
13247         /**
13248          * @event beforeloadadd
13249          * Fires after a new set of Records has been loaded.
13250          * @param {Store} this
13251          * @param {Roo.data.Record[]} records The Records that were loaded
13252          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13253          */
13254         beforeloadadd : true,
13255         /**
13256          * @event load
13257          * Fires after a new set of Records has been loaded, before they are added to the store.
13258          * @param {Store} this
13259          * @param {Roo.data.Record[]} records The Records that were loaded
13260          * @param {Object} options The loading options that were specified (see {@link #load} for details)
13261          * @params {Object} return from reader
13262          */
13263         load : true,
13264         /**
13265          * @event loadexception
13266          * Fires if an exception occurs in the Proxy during loading.
13267          * Called with the signature of the Proxy's "loadexception" event.
13268          * If you return Json { data: [] , success: false, .... } then this will be thrown with the following args
13269          * 
13270          * @param {Proxy} 
13271          * @param {Object} return from JsonData.reader() - success, totalRecords, records
13272          * @param {Object} load options 
13273          * @param {Object} jsonData from your request (normally this contains the Exception)
13274          */
13275         loadexception : true
13276     });
13277     
13278     if(this.proxy){
13279         this.proxy = Roo.factory(this.proxy, Roo.data);
13280         this.proxy.xmodule = this.xmodule || false;
13281         this.relayEvents(this.proxy,  ["loadexception"]);
13282     }
13283     this.sortToggle = {};
13284     this.sortOrder = []; // array of order of sorting - updated by grid if multisort is enabled.
13285
13286     Roo.data.Store.superclass.constructor.call(this);
13287
13288     if(this.inlineData){
13289         this.loadData(this.inlineData);
13290         delete this.inlineData;
13291     }
13292 };
13293
13294 Roo.extend(Roo.data.Store, Roo.util.Observable, {
13295      /**
13296     * @cfg {boolean} isLocal   flag if data is locally available (and can be always looked up
13297     * without a remote query - used by combo/forms at present.
13298     */
13299     
13300     /**
13301     * @cfg {Roo.data.DataProxy} proxy The Proxy object which provides access to a data object.
13302     */
13303     /**
13304     * @cfg {Array} data Inline data to be loaded when the store is initialized.
13305     */
13306     /**
13307     * @cfg {Roo.data.Reader} reader The Reader object which processes the data object and returns
13308     * an Array of Roo.data.record objects which are cached keyed by their <em>id</em> property.
13309     */
13310     /**
13311     * @cfg {Object} baseParams An object containing properties which are to be sent as parameters
13312     * on any HTTP request
13313     */
13314     /**
13315     * @cfg {Object} sortInfo A config object in the format: {field: "fieldName", direction: "ASC|DESC"}
13316     */
13317     /**
13318     * @cfg {Boolean} multiSort enable multi column sorting (sort is based on the order of columns, remote only at present)
13319     */
13320     multiSort: false,
13321     /**
13322     * @cfg {boolean} remoteSort True if sorting is to be handled by requesting the Proxy to provide a refreshed
13323     * version of the data object in sorted order, as opposed to sorting the Record cache in place (defaults to false).
13324     */
13325     remoteSort : false,
13326
13327     /**
13328     * @cfg {boolean} pruneModifiedRecords True to clear all modified record information each time the store is
13329      * loaded or when a record is removed. (defaults to false).
13330     */
13331     pruneModifiedRecords : false,
13332
13333     // private
13334     lastOptions : null,
13335
13336     /**
13337      * Add Records to the Store and fires the add event.
13338      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13339      */
13340     add : function(records){
13341         records = [].concat(records);
13342         for(var i = 0, len = records.length; i < len; i++){
13343             records[i].join(this);
13344         }
13345         var index = this.data.length;
13346         this.data.addAll(records);
13347         this.fireEvent("add", this, records, index);
13348     },
13349
13350     /**
13351      * Remove a Record from the Store and fires the remove event.
13352      * @param {Ext.data.Record} record The Roo.data.Record object to remove from the cache.
13353      */
13354     remove : function(record){
13355         var index = this.data.indexOf(record);
13356         this.data.removeAt(index);
13357  
13358         if(this.pruneModifiedRecords){
13359             this.modified.remove(record);
13360         }
13361         this.fireEvent("remove", this, record, index);
13362     },
13363
13364     /**
13365      * Remove all Records from the Store and fires the clear event.
13366      */
13367     removeAll : function(){
13368         this.data.clear();
13369         if(this.pruneModifiedRecords){
13370             this.modified = [];
13371         }
13372         this.fireEvent("clear", this);
13373     },
13374
13375     /**
13376      * Inserts Records to the Store at the given index and fires the add event.
13377      * @param {Number} index The start index at which to insert the passed Records.
13378      * @param {Roo.data.Record[]} records An Array of Roo.data.Record objects to add to the cache.
13379      */
13380     insert : function(index, records){
13381         records = [].concat(records);
13382         for(var i = 0, len = records.length; i < len; i++){
13383             this.data.insert(index, records[i]);
13384             records[i].join(this);
13385         }
13386         this.fireEvent("add", this, records, index);
13387     },
13388
13389     /**
13390      * Get the index within the cache of the passed Record.
13391      * @param {Roo.data.Record} record The Roo.data.Record object to to find.
13392      * @return {Number} The index of the passed Record. Returns -1 if not found.
13393      */
13394     indexOf : function(record){
13395         return this.data.indexOf(record);
13396     },
13397
13398     /**
13399      * Get the index within the cache of the Record with the passed id.
13400      * @param {String} id The id of the Record to find.
13401      * @return {Number} The index of the Record. Returns -1 if not found.
13402      */
13403     indexOfId : function(id){
13404         return this.data.indexOfKey(id);
13405     },
13406
13407     /**
13408      * Get the Record with the specified id.
13409      * @param {String} id The id of the Record to find.
13410      * @return {Roo.data.Record} The Record with the passed id. Returns undefined if not found.
13411      */
13412     getById : function(id){
13413         return this.data.key(id);
13414     },
13415
13416     /**
13417      * Get the Record at the specified index.
13418      * @param {Number} index The index of the Record to find.
13419      * @return {Roo.data.Record} The Record at the passed index. Returns undefined if not found.
13420      */
13421     getAt : function(index){
13422         return this.data.itemAt(index);
13423     },
13424
13425     /**
13426      * Returns a range of Records between specified indices.
13427      * @param {Number} startIndex (optional) The starting index (defaults to 0)
13428      * @param {Number} endIndex (optional) The ending index (defaults to the last Record in the Store)
13429      * @return {Roo.data.Record[]} An array of Records
13430      */
13431     getRange : function(start, end){
13432         return this.data.getRange(start, end);
13433     },
13434
13435     // private
13436     storeOptions : function(o){
13437         o = Roo.apply({}, o);
13438         delete o.callback;
13439         delete o.scope;
13440         this.lastOptions = o;
13441     },
13442
13443     /**
13444      * Loads the Record cache from the configured Proxy using the configured Reader.
13445      * <p>
13446      * If using remote paging, then the first load call must specify the <em>start</em>
13447      * and <em>limit</em> properties in the options.params property to establish the initial
13448      * position within the dataset, and the number of Records to cache on each read from the Proxy.
13449      * <p>
13450      * <strong>It is important to note that for remote data sources, loading is asynchronous,
13451      * and this call will return before the new data has been loaded. Perform any post-processing
13452      * in a callback function, or in a "load" event handler.</strong>
13453      * <p>
13454      * @param {Object} options An object containing properties which control loading options:<ul>
13455      * <li>params {Object} An object containing properties to pass as HTTP parameters to a remote data source.</li>
13456      * <li>callback {Function} A function to be called after the Records have been loaded. The callback is
13457      * passed the following arguments:<ul>
13458      * <li>r : Roo.data.Record[]</li>
13459      * <li>options: Options object from the load call</li>
13460      * <li>success: Boolean success indicator</li></ul></li>
13461      * <li>scope {Object} Scope with which to call the callback (defaults to the Store object)</li>
13462      * <li>add {Boolean} indicator to append loaded records rather than replace the current cache.</li>
13463      * </ul>
13464      */
13465     load : function(options){
13466         options = options || {};
13467         if(this.fireEvent("beforeload", this, options) !== false){
13468             this.storeOptions(options);
13469             var p = Roo.apply(options.params || {}, this.baseParams);
13470             // if meta was not loaded from remote source.. try requesting it.
13471             if (!this.reader.metaFromRemote) {
13472                 p._requestMeta = 1;
13473             }
13474             if(this.sortInfo && this.remoteSort){
13475                 var pn = this.paramNames;
13476                 p[pn["sort"]] = this.sortInfo.field;
13477                 p[pn["dir"]] = this.sortInfo.direction;
13478             }
13479             if (this.multiSort) {
13480                 var pn = this.paramNames;
13481                 p[pn["multisort"]] = Roo.encode( { sort : this.sortToggle, order: this.sortOrder });
13482             }
13483             
13484             this.proxy.load(p, this.reader, this.loadRecords, this, options);
13485         }
13486     },
13487
13488     /**
13489      * Reloads the Record cache from the configured Proxy using the configured Reader and
13490      * the options from the last load operation performed.
13491      * @param {Object} options (optional) An object containing properties which may override the options
13492      * used in the last load operation. See {@link #load} for details (defaults to null, in which case
13493      * the most recently used options are reused).
13494      */
13495     reload : function(options){
13496         this.load(Roo.applyIf(options||{}, this.lastOptions));
13497     },
13498
13499     // private
13500     // Called as a callback by the Reader during a load operation.
13501     loadRecords : function(o, options, success){
13502         if(!o || success === false){
13503             if(success !== false){
13504                 this.fireEvent("load", this, [], options, o);
13505             }
13506             if(options.callback){
13507                 options.callback.call(options.scope || this, [], options, false);
13508             }
13509             return;
13510         }
13511         // if data returned failure - throw an exception.
13512         if (o.success === false) {
13513             // show a message if no listener is registered.
13514             if (!this.hasListener('loadexception') && typeof(o.raw.errorMsg) != 'undefined') {
13515                     Roo.MessageBox.alert("Error loading",o.raw.errorMsg);
13516             }
13517             // loadmask wil be hooked into this..
13518             this.fireEvent("loadexception", this, o, options, o.raw.errorMsg);
13519             return;
13520         }
13521         var r = o.records, t = o.totalRecords || r.length;
13522         
13523         this.fireEvent("beforeloadadd", this, r, options, o);
13524         
13525         if(!options || options.add !== true){
13526             if(this.pruneModifiedRecords){
13527                 this.modified = [];
13528             }
13529             for(var i = 0, len = r.length; i < len; i++){
13530                 r[i].join(this);
13531             }
13532             if(this.snapshot){
13533                 this.data = this.snapshot;
13534                 delete this.snapshot;
13535             }
13536             this.data.clear();
13537             this.data.addAll(r);
13538             this.totalLength = t;
13539             this.applySort();
13540             this.fireEvent("datachanged", this);
13541         }else{
13542             this.totalLength = Math.max(t, this.data.length+r.length);
13543             this.add(r);
13544         }
13545         
13546         if(this.parent && !Roo.isIOS && !this.useNativeIOS && this.parent.emptyTitle.length) {
13547                 
13548             var e = new Roo.data.Record({});
13549
13550             e.set(this.parent.displayField, this.parent.emptyTitle);
13551             e.set(this.parent.valueField, '');
13552
13553             this.insert(0, e);
13554         }
13555             
13556         this.fireEvent("load", this, r, options, o);
13557         if(options.callback){
13558             options.callback.call(options.scope || this, r, options, true);
13559         }
13560     },
13561
13562
13563     /**
13564      * Loads data from a passed data block. A Reader which understands the format of the data
13565      * must have been configured in the constructor.
13566      * @param {Object} data The data block from which to read the Records.  The format of the data expected
13567      * is dependent on the type of Reader that is configured and should correspond to that Reader's readRecords parameter.
13568      * @param {Boolean} append (Optional) True to append the new Records rather than replace the existing cache.
13569      */
13570     loadData : function(o, append){
13571         var r = this.reader.readRecords(o);
13572         this.loadRecords(r, {add: append}, true);
13573     },
13574     
13575      /**
13576      * using 'cn' the nested child reader read the child array into it's child stores.
13577      * @param {Object} rec The record with a 'children array
13578      */
13579     loadDataFromChildren : function(rec)
13580     {
13581         this.loadData(this.reader.toLoadData(rec));
13582     },
13583     
13584
13585     /**
13586      * Gets the number of cached records.
13587      * <p>
13588      * <em>If using paging, this may not be the total size of the dataset. If the data object
13589      * used by the Reader contains the dataset size, then the getTotalCount() function returns
13590      * the data set size</em>
13591      */
13592     getCount : function(){
13593         return this.data.length || 0;
13594     },
13595
13596     /**
13597      * Gets the total number of records in the dataset as returned by the server.
13598      * <p>
13599      * <em>If using paging, for this to be accurate, the data object used by the Reader must contain
13600      * the dataset size</em>
13601      */
13602     getTotalCount : function(){
13603         return this.totalLength || 0;
13604     },
13605
13606     /**
13607      * Returns the sort state of the Store as an object with two properties:
13608      * <pre><code>
13609  field {String} The name of the field by which the Records are sorted
13610  direction {String} The sort order, "ASC" or "DESC"
13611      * </code></pre>
13612      */
13613     getSortState : function(){
13614         return this.sortInfo;
13615     },
13616
13617     // private
13618     applySort : function(){
13619         if(this.sortInfo && !this.remoteSort){
13620             var s = this.sortInfo, f = s.field;
13621             var st = this.fields.get(f).sortType;
13622             var fn = function(r1, r2){
13623                 var v1 = st(r1.data[f]), v2 = st(r2.data[f]);
13624                 return v1 > v2 ? 1 : (v1 < v2 ? -1 : 0);
13625             };
13626             this.data.sort(s.direction, fn);
13627             if(this.snapshot && this.snapshot != this.data){
13628                 this.snapshot.sort(s.direction, fn);
13629             }
13630         }
13631     },
13632
13633     /**
13634      * Sets the default sort column and order to be used by the next load operation.
13635      * @param {String} fieldName The name of the field to sort by.
13636      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13637      */
13638     setDefaultSort : function(field, dir){
13639         this.sortInfo = {field: field, direction: dir ? dir.toUpperCase() : "ASC"};
13640     },
13641
13642     /**
13643      * Sort the Records.
13644      * If remote sorting is used, the sort is performed on the server, and the cache is
13645      * reloaded. If local sorting is used, the cache is sorted internally.
13646      * @param {String} fieldName The name of the field to sort by.
13647      * @param {String} dir (optional) The sort order, "ASC" or "DESC" (defaults to "ASC")
13648      */
13649     sort : function(fieldName, dir){
13650         var f = this.fields.get(fieldName);
13651         if(!dir){
13652             this.sortToggle[f.name] = this.sortToggle[f.name] || f.sortDir;
13653             
13654             if(this.multiSort || (this.sortInfo && this.sortInfo.field == f.name) ){ // toggle sort dir
13655                 dir = (this.sortToggle[f.name] || "ASC").toggle("ASC", "DESC");
13656             }else{
13657                 dir = f.sortDir;
13658             }
13659         }
13660         this.sortToggle[f.name] = dir;
13661         this.sortInfo = {field: f.name, direction: dir};
13662         if(!this.remoteSort){
13663             this.applySort();
13664             this.fireEvent("datachanged", this);
13665         }else{
13666             this.load(this.lastOptions);
13667         }
13668     },
13669
13670     /**
13671      * Calls the specified function for each of the Records in the cache.
13672      * @param {Function} fn The function to call. The Record is passed as the first parameter.
13673      * Returning <em>false</em> aborts and exits the iteration.
13674      * @param {Object} scope (optional) The scope in which to call the function (defaults to the Record).
13675      */
13676     each : function(fn, scope){
13677         this.data.each(fn, scope);
13678     },
13679
13680     /**
13681      * Gets all records modified since the last commit.  Modified records are persisted across load operations
13682      * (e.g., during paging).
13683      * @return {Roo.data.Record[]} An array of Records containing outstanding modifications.
13684      */
13685     getModifiedRecords : function(){
13686         return this.modified;
13687     },
13688
13689     // private
13690     createFilterFn : function(property, value, anyMatch){
13691         if(!value.exec){ // not a regex
13692             value = String(value);
13693             if(value.length == 0){
13694                 return false;
13695             }
13696             value = new RegExp((anyMatch === true ? '' : '^') + Roo.escapeRe(value), "i");
13697         }
13698         return function(r){
13699             return value.test(r.data[property]);
13700         };
13701     },
13702
13703     /**
13704      * Sums the value of <i>property</i> for each record between start and end and returns the result.
13705      * @param {String} property A field on your records
13706      * @param {Number} start The record index to start at (defaults to 0)
13707      * @param {Number} end The last record index to include (defaults to length - 1)
13708      * @return {Number} The sum
13709      */
13710     sum : function(property, start, end){
13711         var rs = this.data.items, v = 0;
13712         start = start || 0;
13713         end = (end || end === 0) ? end : rs.length-1;
13714
13715         for(var i = start; i <= end; i++){
13716             v += (rs[i].data[property] || 0);
13717         }
13718         return v;
13719     },
13720
13721     /**
13722      * Filter the records by a specified property.
13723      * @param {String} field A field on your records
13724      * @param {String/RegExp} value Either a string that the field
13725      * should start with or a RegExp to test against the field
13726      * @param {Boolean} anyMatch True to match any part not just the beginning
13727      */
13728     filter : function(property, value, anyMatch){
13729         var fn = this.createFilterFn(property, value, anyMatch);
13730         return fn ? this.filterBy(fn) : this.clearFilter();
13731     },
13732
13733     /**
13734      * Filter by a function. The specified function will be called with each
13735      * record in this data source. If the function returns true the record is included,
13736      * otherwise it is filtered.
13737      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13738      * @param {Object} scope (optional) The scope of the function (defaults to this)
13739      */
13740     filterBy : function(fn, scope){
13741         this.snapshot = this.snapshot || this.data;
13742         this.data = this.queryBy(fn, scope||this);
13743         this.fireEvent("datachanged", this);
13744     },
13745
13746     /**
13747      * Query the records by a specified property.
13748      * @param {String} field A field on your records
13749      * @param {String/RegExp} value Either a string that the field
13750      * should start with or a RegExp to test against the field
13751      * @param {Boolean} anyMatch True to match any part not just the beginning
13752      * @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13753      */
13754     query : function(property, value, anyMatch){
13755         var fn = this.createFilterFn(property, value, anyMatch);
13756         return fn ? this.queryBy(fn) : this.data.clone();
13757     },
13758
13759     /**
13760      * Query by a function. The specified function will be called with each
13761      * record in this data source. If the function returns true the record is included
13762      * in the results.
13763      * @param {Function} fn The function to be called, it will receive 2 args (record, id)
13764      * @param {Object} scope (optional) The scope of the function (defaults to this)
13765       @return {MixedCollection} Returns an Roo.util.MixedCollection of the matched records
13766      **/
13767     queryBy : function(fn, scope){
13768         var data = this.snapshot || this.data;
13769         return data.filterBy(fn, scope||this);
13770     },
13771
13772     /**
13773      * Collects unique values for a particular dataIndex from this store.
13774      * @param {String} dataIndex The property to collect
13775      * @param {Boolean} allowNull (optional) Pass true to allow null, undefined or empty string values
13776      * @param {Boolean} bypassFilter (optional) Pass true to collect from all records, even ones which are filtered
13777      * @return {Array} An array of the unique values
13778      **/
13779     collect : function(dataIndex, allowNull, bypassFilter){
13780         var d = (bypassFilter === true && this.snapshot) ?
13781                 this.snapshot.items : this.data.items;
13782         var v, sv, r = [], l = {};
13783         for(var i = 0, len = d.length; i < len; i++){
13784             v = d[i].data[dataIndex];
13785             sv = String(v);
13786             if((allowNull || !Roo.isEmpty(v)) && !l[sv]){
13787                 l[sv] = true;
13788                 r[r.length] = v;
13789             }
13790         }
13791         return r;
13792     },
13793
13794     /**
13795      * Revert to a view of the Record cache with no filtering applied.
13796      * @param {Boolean} suppressEvent If true the filter is cleared silently without notifying listeners
13797      */
13798     clearFilter : function(suppressEvent){
13799         if(this.snapshot && this.snapshot != this.data){
13800             this.data = this.snapshot;
13801             delete this.snapshot;
13802             if(suppressEvent !== true){
13803                 this.fireEvent("datachanged", this);
13804             }
13805         }
13806     },
13807
13808     // private
13809     afterEdit : function(record){
13810         if(this.modified.indexOf(record) == -1){
13811             this.modified.push(record);
13812         }
13813         this.fireEvent("update", this, record, Roo.data.Record.EDIT);
13814     },
13815     
13816     // private
13817     afterReject : function(record){
13818         this.modified.remove(record);
13819         this.fireEvent("update", this, record, Roo.data.Record.REJECT);
13820     },
13821
13822     // private
13823     afterCommit : function(record){
13824         this.modified.remove(record);
13825         this.fireEvent("update", this, record, Roo.data.Record.COMMIT);
13826     },
13827
13828     /**
13829      * Commit all Records with outstanding changes. To handle updates for changes, subscribe to the
13830      * Store's "update" event, and perform updating when the third parameter is Roo.data.Record.COMMIT.
13831      */
13832     commitChanges : function(){
13833         var m = this.modified.slice(0);
13834         this.modified = [];
13835         for(var i = 0, len = m.length; i < len; i++){
13836             m[i].commit();
13837         }
13838     },
13839
13840     /**
13841      * Cancel outstanding changes on all changed records.
13842      */
13843     rejectChanges : function(){
13844         var m = this.modified.slice(0);
13845         this.modified = [];
13846         for(var i = 0, len = m.length; i < len; i++){
13847             m[i].reject();
13848         }
13849     },
13850
13851     onMetaChange : function(meta, rtype, o){
13852         this.recordType = rtype;
13853         this.fields = rtype.prototype.fields;
13854         delete this.snapshot;
13855         this.sortInfo = meta.sortInfo || this.sortInfo;
13856         this.modified = [];
13857         this.fireEvent('metachange', this, this.reader.meta);
13858     },
13859     
13860     moveIndex : function(data, type)
13861     {
13862         var index = this.indexOf(data);
13863         
13864         var newIndex = index + type;
13865         
13866         this.remove(data);
13867         
13868         this.insert(newIndex, data);
13869         
13870     }
13871 });/*
13872  * Based on:
13873  * Ext JS Library 1.1.1
13874  * Copyright(c) 2006-2007, Ext JS, LLC.
13875  *
13876  * Originally Released Under LGPL - original licence link has changed is not relivant.
13877  *
13878  * Fork - LGPL
13879  * <script type="text/javascript">
13880  */
13881
13882 /**
13883  * @class Roo.data.SimpleStore
13884  * @extends Roo.data.Store
13885  * Small helper class to make creating Stores from Array data easier.
13886  * @cfg {Number} id The array index of the record id. Leave blank to auto generate ids.
13887  * @cfg {Array} fields An array of field definition objects, or field name strings.
13888  * @cfg {Object} an existing reader (eg. copied from another store)
13889  * @cfg {Array} data The multi-dimensional array of data
13890  * @constructor
13891  * @param {Object} config
13892  */
13893 Roo.data.SimpleStore = function(config)
13894 {
13895     Roo.data.SimpleStore.superclass.constructor.call(this, {
13896         isLocal : true,
13897         reader: typeof(config.reader) != 'undefined' ? config.reader : new Roo.data.ArrayReader({
13898                 id: config.id
13899             },
13900             Roo.data.Record.create(config.fields)
13901         ),
13902         proxy : new Roo.data.MemoryProxy(config.data)
13903     });
13904     this.load();
13905 };
13906 Roo.extend(Roo.data.SimpleStore, Roo.data.Store);/*
13907  * Based on:
13908  * Ext JS Library 1.1.1
13909  * Copyright(c) 2006-2007, Ext JS, LLC.
13910  *
13911  * Originally Released Under LGPL - original licence link has changed is not relivant.
13912  *
13913  * Fork - LGPL
13914  * <script type="text/javascript">
13915  */
13916
13917 /**
13918 /**
13919  * @extends Roo.data.Store
13920  * @class Roo.data.JsonStore
13921  * Small helper class to make creating Stores for JSON data easier. <br/>
13922 <pre><code>
13923 var store = new Roo.data.JsonStore({
13924     url: 'get-images.php',
13925     root: 'images',
13926     fields: ['name', 'url', {name:'size', type: 'float'}, {name:'lastmod', type:'date'}]
13927 });
13928 </code></pre>
13929  * <b>Note: Although they are not listed, this class inherits all of the config options of Store,
13930  * JsonReader and HttpProxy (unless inline data is provided).</b>
13931  * @cfg {Array} fields An array of field definition objects, or field name strings.
13932  * @constructor
13933  * @param {Object} config
13934  */
13935 Roo.data.JsonStore = function(c){
13936     Roo.data.JsonStore.superclass.constructor.call(this, Roo.apply(c, {
13937         proxy: !c.data ? new Roo.data.HttpProxy({url: c.url}) : undefined,
13938         reader: new Roo.data.JsonReader(c, c.fields)
13939     }));
13940 };
13941 Roo.extend(Roo.data.JsonStore, Roo.data.Store);/*
13942  * Based on:
13943  * Ext JS Library 1.1.1
13944  * Copyright(c) 2006-2007, Ext JS, LLC.
13945  *
13946  * Originally Released Under LGPL - original licence link has changed is not relivant.
13947  *
13948  * Fork - LGPL
13949  * <script type="text/javascript">
13950  */
13951
13952  
13953 Roo.data.Field = function(config){
13954     if(typeof config == "string"){
13955         config = {name: config};
13956     }
13957     Roo.apply(this, config);
13958     
13959     if(!this.type){
13960         this.type = "auto";
13961     }
13962     
13963     var st = Roo.data.SortTypes;
13964     // named sortTypes are supported, here we look them up
13965     if(typeof this.sortType == "string"){
13966         this.sortType = st[this.sortType];
13967     }
13968     
13969     // set default sortType for strings and dates
13970     if(!this.sortType){
13971         switch(this.type){
13972             case "string":
13973                 this.sortType = st.asUCString;
13974                 break;
13975             case "date":
13976                 this.sortType = st.asDate;
13977                 break;
13978             default:
13979                 this.sortType = st.none;
13980         }
13981     }
13982
13983     // define once
13984     var stripRe = /[\$,%]/g;
13985
13986     // prebuilt conversion function for this field, instead of
13987     // switching every time we're reading a value
13988     if(!this.convert){
13989         var cv, dateFormat = this.dateFormat;
13990         switch(this.type){
13991             case "":
13992             case "auto":
13993             case undefined:
13994                 cv = function(v){ return v; };
13995                 break;
13996             case "string":
13997                 cv = function(v){ return (v === undefined || v === null) ? '' : String(v); };
13998                 break;
13999             case "int":
14000                 cv = function(v){
14001                     return v !== undefined && v !== null && v !== '' ?
14002                            parseInt(String(v).replace(stripRe, ""), 10) : '';
14003                     };
14004                 break;
14005             case "float":
14006                 cv = function(v){
14007                     return v !== undefined && v !== null && v !== '' ?
14008                            parseFloat(String(v).replace(stripRe, ""), 10) : ''; 
14009                     };
14010                 break;
14011             case "bool":
14012             case "boolean":
14013                 cv = function(v){ return v === true || v === "true" || v == 1; };
14014                 break;
14015             case "date":
14016                 cv = function(v){
14017                     if(!v){
14018                         return '';
14019                     }
14020                     if(v instanceof Date){
14021                         return v;
14022                     }
14023                     if(dateFormat){
14024                         if(dateFormat == "timestamp"){
14025                             return new Date(v*1000);
14026                         }
14027                         return Date.parseDate(v, dateFormat);
14028                     }
14029                     var parsed = Date.parse(v);
14030                     return parsed ? new Date(parsed) : null;
14031                 };
14032              break;
14033             
14034         }
14035         this.convert = cv;
14036     }
14037 };
14038
14039 Roo.data.Field.prototype = {
14040     dateFormat: null,
14041     defaultValue: "",
14042     mapping: null,
14043     sortType : null,
14044     sortDir : "ASC"
14045 };/*
14046  * Based on:
14047  * Ext JS Library 1.1.1
14048  * Copyright(c) 2006-2007, Ext JS, LLC.
14049  *
14050  * Originally Released Under LGPL - original licence link has changed is not relivant.
14051  *
14052  * Fork - LGPL
14053  * <script type="text/javascript">
14054  */
14055  
14056 // Base class for reading structured data from a data source.  This class is intended to be
14057 // extended (see ArrayReader, JsonReader and XmlReader) and should not be created directly.
14058
14059 /**
14060  * @class Roo.data.DataReader
14061  * Base class for reading structured data from a data source.  This class is intended to be
14062  * extended (see {Roo.data.ArrayReader}, {Roo.data.JsonReader} and {Roo.data.XmlReader}) and should not be created directly.
14063  */
14064
14065 Roo.data.DataReader = function(meta, recordType){
14066     
14067     this.meta = meta;
14068     
14069     this.recordType = recordType instanceof Array ? 
14070         Roo.data.Record.create(recordType) : recordType;
14071 };
14072
14073 Roo.data.DataReader.prototype = {
14074     
14075     
14076     readerType : 'Data',
14077      /**
14078      * Create an empty record
14079      * @param {Object} data (optional) - overlay some values
14080      * @return {Roo.data.Record} record created.
14081      */
14082     newRow :  function(d) {
14083         var da =  {};
14084         this.recordType.prototype.fields.each(function(c) {
14085             switch( c.type) {
14086                 case 'int' : da[c.name] = 0; break;
14087                 case 'date' : da[c.name] = new Date(); break;
14088                 case 'float' : da[c.name] = 0.0; break;
14089                 case 'boolean' : da[c.name] = false; break;
14090                 default : da[c.name] = ""; break;
14091             }
14092             
14093         });
14094         return new this.recordType(Roo.apply(da, d));
14095     }
14096     
14097     
14098 };/*
14099  * Based on:
14100  * Ext JS Library 1.1.1
14101  * Copyright(c) 2006-2007, Ext JS, LLC.
14102  *
14103  * Originally Released Under LGPL - original licence link has changed is not relivant.
14104  *
14105  * Fork - LGPL
14106  * <script type="text/javascript">
14107  */
14108
14109 /**
14110  * @class Roo.data.DataProxy
14111  * @extends Roo.data.Observable
14112  * This class is an abstract base class for implementations which provide retrieval of
14113  * unformatted data objects.<br>
14114  * <p>
14115  * DataProxy implementations are usually used in conjunction with an implementation of Roo.data.DataReader
14116  * (of the appropriate type which knows how to parse the data object) to provide a block of
14117  * {@link Roo.data.Records} to an {@link Roo.data.Store}.<br>
14118  * <p>
14119  * Custom implementations must implement the load method as described in
14120  * {@link Roo.data.HttpProxy#load}.
14121  */
14122 Roo.data.DataProxy = function(){
14123     this.addEvents({
14124         /**
14125          * @event beforeload
14126          * Fires before a network request is made to retrieve a data object.
14127          * @param {Object} This DataProxy object.
14128          * @param {Object} params The params parameter to the load function.
14129          */
14130         beforeload : true,
14131         /**
14132          * @event load
14133          * Fires before the load method's callback is called.
14134          * @param {Object} This DataProxy object.
14135          * @param {Object} o The data object.
14136          * @param {Object} arg The callback argument object passed to the load function.
14137          */
14138         load : true,
14139         /**
14140          * @event loadexception
14141          * Fires if an Exception occurs during data retrieval.
14142          * @param {Object} This DataProxy object.
14143          * @param {Object} o The data object.
14144          * @param {Object} arg The callback argument object passed to the load function.
14145          * @param {Object} e The Exception.
14146          */
14147         loadexception : true
14148     });
14149     Roo.data.DataProxy.superclass.constructor.call(this);
14150 };
14151
14152 Roo.extend(Roo.data.DataProxy, Roo.util.Observable);
14153
14154     /**
14155      * @cfg {void} listeners (Not available) Constructor blocks listeners from being set
14156      */
14157 /*
14158  * Based on:
14159  * Ext JS Library 1.1.1
14160  * Copyright(c) 2006-2007, Ext JS, LLC.
14161  *
14162  * Originally Released Under LGPL - original licence link has changed is not relivant.
14163  *
14164  * Fork - LGPL
14165  * <script type="text/javascript">
14166  */
14167 /**
14168  * @class Roo.data.MemoryProxy
14169  * An implementation of Roo.data.DataProxy that simply passes the data specified in its constructor
14170  * to the Reader when its load method is called.
14171  * @constructor
14172  * @param {Object} data The data object which the Reader uses to construct a block of Roo.data.Records.
14173  */
14174 Roo.data.MemoryProxy = function(data){
14175     if (data.data) {
14176         data = data.data;
14177     }
14178     Roo.data.MemoryProxy.superclass.constructor.call(this);
14179     this.data = data;
14180 };
14181
14182 Roo.extend(Roo.data.MemoryProxy, Roo.data.DataProxy, {
14183     
14184     /**
14185      * Load data from the requested source (in this case an in-memory
14186      * data object passed to the constructor), read the data object into
14187      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14188      * process that block using the passed callback.
14189      * @param {Object} params This parameter is not used by the MemoryProxy class.
14190      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14191      * object into a block of Roo.data.Records.
14192      * @param {Function} callback The function into which to pass the block of Roo.data.records.
14193      * The function must be passed <ul>
14194      * <li>The Record block object</li>
14195      * <li>The "arg" argument from the load function</li>
14196      * <li>A boolean success indicator</li>
14197      * </ul>
14198      * @param {Object} scope The scope in which to call the callback
14199      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14200      */
14201     load : function(params, reader, callback, scope, arg){
14202         params = params || {};
14203         var result;
14204         try {
14205             result = reader.readRecords(params.data ? params.data :this.data);
14206         }catch(e){
14207             this.fireEvent("loadexception", this, arg, null, e);
14208             callback.call(scope, null, arg, false);
14209             return;
14210         }
14211         callback.call(scope, result, arg, true);
14212     },
14213     
14214     // private
14215     update : function(params, records){
14216         
14217     }
14218 });/*
14219  * Based on:
14220  * Ext JS Library 1.1.1
14221  * Copyright(c) 2006-2007, Ext JS, LLC.
14222  *
14223  * Originally Released Under LGPL - original licence link has changed is not relivant.
14224  *
14225  * Fork - LGPL
14226  * <script type="text/javascript">
14227  */
14228 /**
14229  * @class Roo.data.HttpProxy
14230  * @extends Roo.data.DataProxy
14231  * An implementation of {@link Roo.data.DataProxy} that reads a data object from an {@link Roo.data.Connection} object
14232  * configured to reference a certain URL.<br><br>
14233  * <p>
14234  * <em>Note that this class cannot be used to retrieve data from a domain other than the domain
14235  * from which the running page was served.<br><br>
14236  * <p>
14237  * For cross-domain access to remote data, use an {@link Roo.data.ScriptTagProxy}.</em><br><br>
14238  * <p>
14239  * Be aware that to enable the browser to parse an XML document, the server must set
14240  * the Content-Type header in the HTTP response to "text/xml".
14241  * @constructor
14242  * @param {Object} conn Connection config options to add to each request (e.g. {url: 'foo.php'} or
14243  * an {@link Roo.data.Connection} object.  If a Connection config is passed, the singleton {@link Roo.Ajax} object
14244  * will be used to make the request.
14245  */
14246 Roo.data.HttpProxy = function(conn){
14247     Roo.data.HttpProxy.superclass.constructor.call(this);
14248     // is conn a conn config or a real conn?
14249     this.conn = conn;
14250     this.useAjax = !conn || !conn.events;
14251   
14252 };
14253
14254 Roo.extend(Roo.data.HttpProxy, Roo.data.DataProxy, {
14255     // thse are take from connection...
14256     
14257     /**
14258      * @cfg {String} url (Optional) The default URL to be used for requests to the server. (defaults to undefined)
14259      */
14260     /**
14261      * @cfg {Object} extraParams (Optional) An object containing properties which are used as
14262      * extra parameters to each request made by this object. (defaults to undefined)
14263      */
14264     /**
14265      * @cfg {Object} defaultHeaders (Optional) An object containing request headers which are added
14266      *  to each request made by this object. (defaults to undefined)
14267      */
14268     /**
14269      * @cfg {String} method (Optional) The default HTTP method to be used for requests. (defaults to undefined; if not set but parms are present will use POST, otherwise GET)
14270      */
14271     /**
14272      * @cfg {Number} timeout (Optional) The timeout in milliseconds to be used for requests. (defaults to 30000)
14273      */
14274      /**
14275      * @cfg {Boolean} autoAbort (Optional) Whether this request should abort any pending requests. (defaults to false)
14276      * @type Boolean
14277      */
14278   
14279
14280     /**
14281      * @cfg {Boolean} disableCaching (Optional) True to add a unique cache-buster param to GET requests. (defaults to true)
14282      * @type Boolean
14283      */
14284     /**
14285      * Return the {@link Roo.data.Connection} object being used by this Proxy.
14286      * @return {Connection} The Connection object. This object may be used to subscribe to events on
14287      * a finer-grained basis than the DataProxy events.
14288      */
14289     getConnection : function(){
14290         return this.useAjax ? Roo.Ajax : this.conn;
14291     },
14292
14293     /**
14294      * Load data from the configured {@link Roo.data.Connection}, read the data object into
14295      * a block of Roo.data.Records using the passed {@link Roo.data.DataReader} implementation, and
14296      * process that block using the passed callback.
14297      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14298      * for the request to the remote server.
14299      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14300      * object into a block of Roo.data.Records.
14301      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14302      * The function must be passed <ul>
14303      * <li>The Record block object</li>
14304      * <li>The "arg" argument from the load function</li>
14305      * <li>A boolean success indicator</li>
14306      * </ul>
14307      * @param {Object} scope The scope in which to call the callback
14308      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14309      */
14310     load : function(params, reader, callback, scope, arg){
14311         if(this.fireEvent("beforeload", this, params) !== false){
14312             var  o = {
14313                 params : params || {},
14314                 request: {
14315                     callback : callback,
14316                     scope : scope,
14317                     arg : arg
14318                 },
14319                 reader: reader,
14320                 callback : this.loadResponse,
14321                 scope: this
14322             };
14323             if(this.useAjax){
14324                 Roo.applyIf(o, this.conn);
14325                 if(this.activeRequest){
14326                     Roo.Ajax.abort(this.activeRequest);
14327                 }
14328                 this.activeRequest = Roo.Ajax.request(o);
14329             }else{
14330                 this.conn.request(o);
14331             }
14332         }else{
14333             callback.call(scope||this, null, arg, false);
14334         }
14335     },
14336
14337     // private
14338     loadResponse : function(o, success, response){
14339         delete this.activeRequest;
14340         if(!success){
14341             this.fireEvent("loadexception", this, o, response);
14342             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14343             return;
14344         }
14345         var result;
14346         try {
14347             result = o.reader.read(response);
14348         }catch(e){
14349             this.fireEvent("loadexception", this, o, response, e);
14350             o.request.callback.call(o.request.scope, null, o.request.arg, false);
14351             return;
14352         }
14353         
14354         this.fireEvent("load", this, o, o.request.arg);
14355         o.request.callback.call(o.request.scope, result, o.request.arg, true);
14356     },
14357
14358     // private
14359     update : function(dataSet){
14360
14361     },
14362
14363     // private
14364     updateResponse : function(dataSet){
14365
14366     }
14367 });/*
14368  * Based on:
14369  * Ext JS Library 1.1.1
14370  * Copyright(c) 2006-2007, Ext JS, LLC.
14371  *
14372  * Originally Released Under LGPL - original licence link has changed is not relivant.
14373  *
14374  * Fork - LGPL
14375  * <script type="text/javascript">
14376  */
14377
14378 /**
14379  * @class Roo.data.ScriptTagProxy
14380  * An implementation of Roo.data.DataProxy that reads a data object from a URL which may be in a domain
14381  * other than the originating domain of the running page.<br><br>
14382  * <p>
14383  * <em>Note that if you are retrieving data from a page that is in a domain that is NOT the same as the originating domain
14384  * of the running page, you must use this class, rather than DataProxy.</em><br><br>
14385  * <p>
14386  * The content passed back from a server resource requested by a ScriptTagProxy is executable JavaScript
14387  * source code that is used as the source inside a &lt;script> tag.<br><br>
14388  * <p>
14389  * In order for the browser to process the returned data, the server must wrap the data object
14390  * with a call to a callback function, the name of which is passed as a parameter by the ScriptTagProxy.
14391  * Below is a Java example for a servlet which returns data for either a ScriptTagProxy, or an HttpProxy
14392  * depending on whether the callback name was passed:
14393  * <p>
14394  * <pre><code>
14395 boolean scriptTag = false;
14396 String cb = request.getParameter("callback");
14397 if (cb != null) {
14398     scriptTag = true;
14399     response.setContentType("text/javascript");
14400 } else {
14401     response.setContentType("application/x-json");
14402 }
14403 Writer out = response.getWriter();
14404 if (scriptTag) {
14405     out.write(cb + "(");
14406 }
14407 out.print(dataBlock.toJsonString());
14408 if (scriptTag) {
14409     out.write(");");
14410 }
14411 </pre></code>
14412  *
14413  * @constructor
14414  * @param {Object} config A configuration object.
14415  */
14416 Roo.data.ScriptTagProxy = function(config){
14417     Roo.data.ScriptTagProxy.superclass.constructor.call(this);
14418     Roo.apply(this, config);
14419     this.head = document.getElementsByTagName("head")[0];
14420 };
14421
14422 Roo.data.ScriptTagProxy.TRANS_ID = 1000;
14423
14424 Roo.extend(Roo.data.ScriptTagProxy, Roo.data.DataProxy, {
14425     /**
14426      * @cfg {String} url The URL from which to request the data object.
14427      */
14428     /**
14429      * @cfg {Number} timeout (Optional) The number of milliseconds to wait for a response. Defaults to 30 seconds.
14430      */
14431     timeout : 30000,
14432     /**
14433      * @cfg {String} callbackParam (Optional) The name of the parameter to pass to the server which tells
14434      * the server the name of the callback function set up by the load call to process the returned data object.
14435      * Defaults to "callback".<p>The server-side processing must read this parameter value, and generate
14436      * javascript output which calls this named function passing the data object as its only parameter.
14437      */
14438     callbackParam : "callback",
14439     /**
14440      *  @cfg {Boolean} nocache (Optional) Defaults to true. Disable cacheing by adding a unique parameter
14441      * name to the request.
14442      */
14443     nocache : true,
14444
14445     /**
14446      * Load data from the configured URL, read the data object into
14447      * a block of Roo.data.Records using the passed Roo.data.DataReader implementation, and
14448      * process that block using the passed callback.
14449      * @param {Object} params An object containing properties which are to be used as HTTP parameters
14450      * for the request to the remote server.
14451      * @param {Roo.data.DataReader} reader The Reader object which converts the data
14452      * object into a block of Roo.data.Records.
14453      * @param {Function} callback The function into which to pass the block of Roo.data.Records.
14454      * The function must be passed <ul>
14455      * <li>The Record block object</li>
14456      * <li>The "arg" argument from the load function</li>
14457      * <li>A boolean success indicator</li>
14458      * </ul>
14459      * @param {Object} scope The scope in which to call the callback
14460      * @param {Object} arg An optional argument which is passed to the callback as its second parameter.
14461      */
14462     load : function(params, reader, callback, scope, arg){
14463         if(this.fireEvent("beforeload", this, params) !== false){
14464
14465             var p = Roo.urlEncode(Roo.apply(params, this.extraParams));
14466
14467             var url = this.url;
14468             url += (url.indexOf("?") != -1 ? "&" : "?") + p;
14469             if(this.nocache){
14470                 url += "&_dc=" + (new Date().getTime());
14471             }
14472             var transId = ++Roo.data.ScriptTagProxy.TRANS_ID;
14473             var trans = {
14474                 id : transId,
14475                 cb : "stcCallback"+transId,
14476                 scriptId : "stcScript"+transId,
14477                 params : params,
14478                 arg : arg,
14479                 url : url,
14480                 callback : callback,
14481                 scope : scope,
14482                 reader : reader
14483             };
14484             var conn = this;
14485
14486             window[trans.cb] = function(o){
14487                 conn.handleResponse(o, trans);
14488             };
14489
14490             url += String.format("&{0}={1}", this.callbackParam, trans.cb);
14491
14492             if(this.autoAbort !== false){
14493                 this.abort();
14494             }
14495
14496             trans.timeoutId = this.handleFailure.defer(this.timeout, this, [trans]);
14497
14498             var script = document.createElement("script");
14499             script.setAttribute("src", url);
14500             script.setAttribute("type", "text/javascript");
14501             script.setAttribute("id", trans.scriptId);
14502             this.head.appendChild(script);
14503
14504             this.trans = trans;
14505         }else{
14506             callback.call(scope||this, null, arg, false);
14507         }
14508     },
14509
14510     // private
14511     isLoading : function(){
14512         return this.trans ? true : false;
14513     },
14514
14515     /**
14516      * Abort the current server request.
14517      */
14518     abort : function(){
14519         if(this.isLoading()){
14520             this.destroyTrans(this.trans);
14521         }
14522     },
14523
14524     // private
14525     destroyTrans : function(trans, isLoaded){
14526         this.head.removeChild(document.getElementById(trans.scriptId));
14527         clearTimeout(trans.timeoutId);
14528         if(isLoaded){
14529             window[trans.cb] = undefined;
14530             try{
14531                 delete window[trans.cb];
14532             }catch(e){}
14533         }else{
14534             // if hasn't been loaded, wait for load to remove it to prevent script error
14535             window[trans.cb] = function(){
14536                 window[trans.cb] = undefined;
14537                 try{
14538                     delete window[trans.cb];
14539                 }catch(e){}
14540             };
14541         }
14542     },
14543
14544     // private
14545     handleResponse : function(o, trans){
14546         this.trans = false;
14547         this.destroyTrans(trans, true);
14548         var result;
14549         try {
14550             result = trans.reader.readRecords(o);
14551         }catch(e){
14552             this.fireEvent("loadexception", this, o, trans.arg, e);
14553             trans.callback.call(trans.scope||window, null, trans.arg, false);
14554             return;
14555         }
14556         this.fireEvent("load", this, o, trans.arg);
14557         trans.callback.call(trans.scope||window, result, trans.arg, true);
14558     },
14559
14560     // private
14561     handleFailure : function(trans){
14562         this.trans = false;
14563         this.destroyTrans(trans, false);
14564         this.fireEvent("loadexception", this, null, trans.arg);
14565         trans.callback.call(trans.scope||window, null, trans.arg, false);
14566     }
14567 });/*
14568  * Based on:
14569  * Ext JS Library 1.1.1
14570  * Copyright(c) 2006-2007, Ext JS, LLC.
14571  *
14572  * Originally Released Under LGPL - original licence link has changed is not relivant.
14573  *
14574  * Fork - LGPL
14575  * <script type="text/javascript">
14576  */
14577
14578 /**
14579  * @class Roo.data.JsonReader
14580  * @extends Roo.data.DataReader
14581  * Data reader class to create an Array of Roo.data.Record objects from a JSON response
14582  * based on mappings in a provided Roo.data.Record constructor.
14583  * 
14584  * The default behaviour of a store is to send ?_requestMeta=1, unless the class has recieved 'metaData' property
14585  * in the reply previously. 
14586  * 
14587  * <p>
14588  * Example code:
14589  * <pre><code>
14590 var RecordDef = Roo.data.Record.create([
14591     {name: 'name', mapping: 'name'},     // "mapping" property not needed if it's the same as "name"
14592     {name: 'occupation'}                 // This field will use "occupation" as the mapping.
14593 ]);
14594 var myReader = new Roo.data.JsonReader({
14595     totalProperty: "results",    // The property which contains the total dataset size (optional)
14596     root: "rows",                // The property which contains an Array of row objects
14597     id: "id"                     // The property within each row object that provides an ID for the record (optional)
14598 }, RecordDef);
14599 </code></pre>
14600  * <p>
14601  * This would consume a JSON file like this:
14602  * <pre><code>
14603 { 'results': 2, 'rows': [
14604     { 'id': 1, 'name': 'Bill', occupation: 'Gardener' },
14605     { 'id': 2, 'name': 'Ben', occupation: 'Horticulturalist' } ]
14606 }
14607 </code></pre>
14608  * @cfg {String} totalProperty Name of the property from which to retrieve the total number of records
14609  * in the dataset. This is only needed if the whole dataset is not passed in one go, but is being
14610  * paged from the remote server.
14611  * @cfg {String} successProperty Name of the property from which to retrieve the success attribute used by forms.
14612  * @cfg {String} root name of the property which contains the Array of row objects.
14613  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14614  * @cfg {Array} fields Array of field definition objects
14615  * @constructor
14616  * Create a new JsonReader
14617  * @param {Object} meta Metadata configuration options
14618  * @param {Object} recordType Either an Array of field definition objects,
14619  * or an {@link Roo.data.Record} object created using {@link Roo.data.Record#create}.
14620  */
14621 Roo.data.JsonReader = function(meta, recordType){
14622     
14623     meta = meta || {};
14624     // set some defaults:
14625     Roo.applyIf(meta, {
14626         totalProperty: 'total',
14627         successProperty : 'success',
14628         root : 'data',
14629         id : 'id'
14630     });
14631     
14632     Roo.data.JsonReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14633 };
14634 Roo.extend(Roo.data.JsonReader, Roo.data.DataReader, {
14635     
14636     readerType : 'Json',
14637     
14638     /**
14639      * @prop {Boolean} metaFromRemote  - if the meta data was loaded from the remote source.
14640      * Used by Store query builder to append _requestMeta to params.
14641      * 
14642      */
14643     metaFromRemote : false,
14644     /**
14645      * This method is only used by a DataProxy which has retrieved data from a remote server.
14646      * @param {Object} response The XHR object which contains the JSON data in its responseText.
14647      * @return {Object} data A data block which is used by an Roo.data.Store object as
14648      * a cache of Roo.data.Records.
14649      */
14650     read : function(response){
14651         var json = response.responseText;
14652        
14653         var o = /* eval:var:o */ eval("("+json+")");
14654         if(!o) {
14655             throw {message: "JsonReader.read: Json object not found"};
14656         }
14657         
14658         if(o.metaData){
14659             
14660             delete this.ef;
14661             this.metaFromRemote = true;
14662             this.meta = o.metaData;
14663             this.recordType = Roo.data.Record.create(o.metaData.fields);
14664             this.onMetaChange(this.meta, this.recordType, o);
14665         }
14666         return this.readRecords(o);
14667     },
14668
14669     // private function a store will implement
14670     onMetaChange : function(meta, recordType, o){
14671
14672     },
14673
14674     /**
14675          * @ignore
14676          */
14677     simpleAccess: function(obj, subsc) {
14678         return obj[subsc];
14679     },
14680
14681         /**
14682          * @ignore
14683          */
14684     getJsonAccessor: function(){
14685         var re = /[\[\.]/;
14686         return function(expr) {
14687             try {
14688                 return(re.test(expr))
14689                     ? new Function("obj", "return obj." + expr)
14690                     : function(obj){
14691                         return obj[expr];
14692                     };
14693             } catch(e){}
14694             return Roo.emptyFn;
14695         };
14696     }(),
14697
14698     /**
14699      * Create a data block containing Roo.data.Records from an XML document.
14700      * @param {Object} o An object which contains an Array of row objects in the property specified
14701      * in the config as 'root, and optionally a property, specified in the config as 'totalProperty'
14702      * which contains the total size of the dataset.
14703      * @return {Object} data A data block which is used by an Roo.data.Store object as
14704      * a cache of Roo.data.Records.
14705      */
14706     readRecords : function(o){
14707         /**
14708          * After any data loads, the raw JSON data is available for further custom processing.
14709          * @type Object
14710          */
14711         this.o = o;
14712         var s = this.meta, Record = this.recordType,
14713             f = Record ? Record.prototype.fields : null, fi = f ? f.items : [], fl = f ? f.length : 0;
14714
14715 //      Generate extraction functions for the totalProperty, the root, the id, and for each field
14716         if (!this.ef) {
14717             if(s.totalProperty) {
14718                     this.getTotal = this.getJsonAccessor(s.totalProperty);
14719                 }
14720                 if(s.successProperty) {
14721                     this.getSuccess = this.getJsonAccessor(s.successProperty);
14722                 }
14723                 this.getRoot = s.root ? this.getJsonAccessor(s.root) : function(p){return p;};
14724                 if (s.id) {
14725                         var g = this.getJsonAccessor(s.id);
14726                         this.getId = function(rec) {
14727                                 var r = g(rec);  
14728                                 return (r === undefined || r === "") ? null : r;
14729                         };
14730                 } else {
14731                         this.getId = function(){return null;};
14732                 }
14733             this.ef = [];
14734             for(var jj = 0; jj < fl; jj++){
14735                 f = fi[jj];
14736                 var map = (f.mapping !== undefined && f.mapping !== null) ? f.mapping : f.name;
14737                 this.ef[jj] = this.getJsonAccessor(map);
14738             }
14739         }
14740
14741         var root = this.getRoot(o), c = root.length, totalRecords = c, success = true;
14742         if(s.totalProperty){
14743             var vt = parseInt(this.getTotal(o), 10);
14744             if(!isNaN(vt)){
14745                 totalRecords = vt;
14746             }
14747         }
14748         if(s.successProperty){
14749             var vs = this.getSuccess(o);
14750             if(vs === false || vs === 'false'){
14751                 success = false;
14752             }
14753         }
14754         var records = [];
14755         for(var i = 0; i < c; i++){
14756                 var n = root[i];
14757             var values = {};
14758             var id = this.getId(n);
14759             for(var j = 0; j < fl; j++){
14760                 f = fi[j];
14761             var v = this.ef[j](n);
14762             if (!f.convert) {
14763                 Roo.log('missing convert for ' + f.name);
14764                 Roo.log(f);
14765                 continue;
14766             }
14767             values[f.name] = f.convert((v !== undefined) ? v : f.defaultValue);
14768             }
14769             var record = new Record(values, id);
14770             record.json = n;
14771             records[i] = record;
14772         }
14773         return {
14774             raw : o,
14775             success : success,
14776             records : records,
14777             totalRecords : totalRecords
14778         };
14779     },
14780     // used when loading children.. @see loadDataFromChildren
14781     toLoadData: function(rec)
14782     {
14783         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14784         var data = typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14785         return { data : data, total : data.length };
14786         
14787     }
14788 });/*
14789  * Based on:
14790  * Ext JS Library 1.1.1
14791  * Copyright(c) 2006-2007, Ext JS, LLC.
14792  *
14793  * Originally Released Under LGPL - original licence link has changed is not relivant.
14794  *
14795  * Fork - LGPL
14796  * <script type="text/javascript">
14797  */
14798
14799 /**
14800  * @class Roo.data.ArrayReader
14801  * @extends Roo.data.DataReader
14802  * Data reader class to create an Array of Roo.data.Record objects from an Array.
14803  * Each element of that Array represents a row of data fields. The
14804  * fields are pulled into a Record object using as a subscript, the <em>mapping</em> property
14805  * of the field definition if it exists, or the field's ordinal position in the definition.<br>
14806  * <p>
14807  * Example code:.
14808  * <pre><code>
14809 var RecordDef = Roo.data.Record.create([
14810     {name: 'name', mapping: 1},         // "mapping" only needed if an "id" field is present which
14811     {name: 'occupation', mapping: 2}    // precludes using the ordinal position as the index.
14812 ]);
14813 var myReader = new Roo.data.ArrayReader({
14814     id: 0                     // The subscript within row Array that provides an ID for the Record (optional)
14815 }, RecordDef);
14816 </code></pre>
14817  * <p>
14818  * This would consume an Array like this:
14819  * <pre><code>
14820 [ [1, 'Bill', 'Gardener'], [2, 'Ben', 'Horticulturalist'] ]
14821   </code></pre>
14822  
14823  * @constructor
14824  * Create a new JsonReader
14825  * @param {Object} meta Metadata configuration options.
14826  * @param {Object|Array} recordType Either an Array of field definition objects
14827  * 
14828  * @cfg {Array} fields Array of field definition objects
14829  * @cfg {String} id Name of the property within a row object that contains a record identifier value.
14830  * as specified to {@link Roo.data.Record#create},
14831  * or an {@link Roo.data.Record} object
14832  *
14833  * 
14834  * created using {@link Roo.data.Record#create}.
14835  */
14836 Roo.data.ArrayReader = function(meta, recordType)
14837 {    
14838     Roo.data.ArrayReader.superclass.constructor.call(this, meta, recordType||meta.fields);
14839 };
14840
14841 Roo.extend(Roo.data.ArrayReader, Roo.data.JsonReader, {
14842     
14843       /**
14844      * Create a data block containing Roo.data.Records from an XML document.
14845      * @param {Object} o An Array of row objects which represents the dataset.
14846      * @return {Object} A data block which is used by an {@link Roo.data.Store} object as
14847      * a cache of Roo.data.Records.
14848      */
14849     readRecords : function(o)
14850     {
14851         var sid = this.meta ? this.meta.id : null;
14852         var recordType = this.recordType, fields = recordType.prototype.fields;
14853         var records = [];
14854         var root = o;
14855         for(var i = 0; i < root.length; i++){
14856                 var n = root[i];
14857             var values = {};
14858             var id = ((sid || sid === 0) && n[sid] !== undefined && n[sid] !== "" ? n[sid] : null);
14859             for(var j = 0, jlen = fields.length; j < jlen; j++){
14860                 var f = fields.items[j];
14861                 var k = f.mapping !== undefined && f.mapping !== null ? f.mapping : j;
14862                 var v = n[k] !== undefined ? n[k] : f.defaultValue;
14863                 v = f.convert(v);
14864                 values[f.name] = v;
14865             }
14866             var record = new recordType(values, id);
14867             record.json = n;
14868             records[records.length] = record;
14869         }
14870         return {
14871             records : records,
14872             totalRecords : records.length
14873         };
14874     },
14875     // used when loading children.. @see loadDataFromChildren
14876     toLoadData: function(rec)
14877     {
14878         // expect rec just to be an array.. eg [a,b,c, [...] << cn ]
14879         return typeof(rec.data.cn) == 'undefined' ? [] : rec.data.cn;
14880         
14881     }
14882     
14883     
14884 });/*
14885  * - LGPL
14886  * * 
14887  */
14888
14889 /**
14890  * @class Roo.bootstrap.ComboBox
14891  * @extends Roo.bootstrap.TriggerField
14892  * A combobox control with support for autocomplete, remote-loading, paging and many other features.
14893  * @cfg {Boolean} append (true|false) default false
14894  * @cfg {Boolean} autoFocus (true|false) auto focus the first item, default true
14895  * @cfg {Boolean} tickable ComboBox with tickable selections (true|false), default false
14896  * @cfg {Boolean} triggerList trigger show the list or not (true|false) default true
14897  * @cfg {Boolean} showToggleBtn show toggle button or not (true|false) default true
14898  * @cfg {String} btnPosition set the position of the trigger button (left | right) default right
14899  * @cfg {Boolean} animate default true
14900  * @cfg {Boolean} emptyResultText only for touch device
14901  * @cfg {String} triggerText multiple combobox trigger button text default 'Select'
14902  * @cfg {String} emptyTitle default ''
14903  * @cfg {Number} width fixed with? experimental
14904  * @constructor
14905  * Create a new ComboBox.
14906  * @param {Object} config Configuration options
14907  */
14908 Roo.bootstrap.ComboBox = function(config){
14909     Roo.bootstrap.ComboBox.superclass.constructor.call(this, config);
14910     this.addEvents({
14911         /**
14912          * @event expand
14913          * Fires when the dropdown list is expanded
14914         * @param {Roo.bootstrap.ComboBox} combo This combo box
14915         */
14916         'expand' : true,
14917         /**
14918          * @event collapse
14919          * Fires when the dropdown list is collapsed
14920         * @param {Roo.bootstrap.ComboBox} combo This combo box
14921         */
14922         'collapse' : true,
14923         /**
14924          * @event beforeselect
14925          * Fires before a list item is selected. Return false to cancel the selection.
14926         * @param {Roo.bootstrap.ComboBox} combo This combo box
14927         * @param {Roo.data.Record} record The data record returned from the underlying store
14928         * @param {Number} index The index of the selected item in the dropdown list
14929         */
14930         'beforeselect' : true,
14931         /**
14932          * @event select
14933          * Fires when a list item is selected
14934         * @param {Roo.bootstrap.ComboBox} combo This combo box
14935         * @param {Roo.data.Record} record The data record returned from the underlying store (or false on clear)
14936         * @param {Number} index The index of the selected item in the dropdown list
14937         */
14938         'select' : true,
14939         /**
14940          * @event beforequery
14941          * Fires before all queries are processed. Return false to cancel the query or set cancel to true.
14942          * The event object passed has these properties:
14943         * @param {Roo.bootstrap.ComboBox} combo This combo box
14944         * @param {String} query The query
14945         * @param {Boolean} forceAll true to force "all" query
14946         * @param {Boolean} cancel true to cancel the query
14947         * @param {Object} e The query event object
14948         */
14949         'beforequery': true,
14950          /**
14951          * @event add
14952          * Fires when the 'add' icon is pressed (add a listener to enable add button)
14953         * @param {Roo.bootstrap.ComboBox} combo This combo box
14954         */
14955         'add' : true,
14956         /**
14957          * @event edit
14958          * Fires when the 'edit' icon is pressed (add a listener to enable add button)
14959         * @param {Roo.bootstrap.ComboBox} combo This combo box
14960         * @param {Roo.data.Record|false} record The data record returned from the underlying store (or false on nothing selected)
14961         */
14962         'edit' : true,
14963         /**
14964          * @event remove
14965          * Fires when the remove value from the combobox array
14966         * @param {Roo.bootstrap.ComboBox} combo This combo box
14967         */
14968         'remove' : true,
14969         /**
14970          * @event afterremove
14971          * Fires when the remove value from the combobox array
14972         * @param {Roo.bootstrap.ComboBox} combo This combo box
14973         */
14974         'afterremove' : true,
14975         /**
14976          * @event specialfilter
14977          * Fires when specialfilter
14978             * @param {Roo.bootstrap.ComboBox} combo This combo box
14979             */
14980         'specialfilter' : true,
14981         /**
14982          * @event tick
14983          * Fires when tick the element
14984             * @param {Roo.bootstrap.ComboBox} combo This combo box
14985             */
14986         'tick' : true,
14987         /**
14988          * @event touchviewdisplay
14989          * Fires when touch view require special display (default is using displayField)
14990             * @param {Roo.bootstrap.ComboBox} combo This combo box
14991             * @param {Object} cfg set html .
14992             */
14993         'touchviewdisplay' : true
14994         
14995     });
14996     
14997     this.item = [];
14998     this.tickItems = [];
14999     
15000     this.selectedIndex = -1;
15001     if(this.mode == 'local'){
15002         if(config.queryDelay === undefined){
15003             this.queryDelay = 10;
15004         }
15005         if(config.minChars === undefined){
15006             this.minChars = 0;
15007         }
15008     }
15009 };
15010
15011 Roo.extend(Roo.bootstrap.ComboBox, Roo.bootstrap.TriggerField, {
15012      
15013     /**
15014      * @cfg {Boolean} lazyRender True to prevent the ComboBox from rendering until requested (should always be used when
15015      * rendering into an Roo.Editor, defaults to false)
15016      */
15017     /**
15018      * @cfg {Boolean/Object} autoCreate A DomHelper element spec, or true for a default element spec (defaults to:
15019      * {tag: "input", type: "text", size: "24", autocomplete: "off"})
15020      */
15021     /**
15022      * @cfg {Roo.data.Store} store The data store to which this combo is bound (defaults to undefined)
15023      */
15024     /**
15025      * @cfg {String} title If supplied, a header element is created containing this text and added into the top of
15026      * the dropdown list (defaults to undefined, with no header element)
15027      */
15028
15029      /**
15030      * @cfg {String/Roo.Template} tpl The template to use to render the output default is  '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' 
15031      */
15032      
15033      /**
15034      * @cfg {Number} listWidth The width in pixels of the dropdown list (defaults to the width of the ComboBox field)
15035      */
15036     listWidth: undefined,
15037     /**
15038      * @cfg {String} displayField The underlying data field name to bind to this CombBox (defaults to undefined if
15039      * mode = 'remote' or 'text' if mode = 'local')
15040      */
15041     displayField: undefined,
15042     
15043     /**
15044      * @cfg {String} valueField The underlying data value name to bind to this CombBox (defaults to undefined if
15045      * mode = 'remote' or 'value' if mode = 'local'). 
15046      * Note: use of a valueField requires the user make a selection
15047      * in order for a value to be mapped.
15048      */
15049     valueField: undefined,
15050     /**
15051      * @cfg {String} modalTitle The title of the dialog that pops up on mobile views.
15052      */
15053     modalTitle : '',
15054     
15055     /**
15056      * @cfg {String} hiddenName If specified, a hidden form field with this name is dynamically generated to store the
15057      * field's data value (defaults to the underlying DOM element's name)
15058      */
15059     hiddenName: undefined,
15060     /**
15061      * @cfg {String} listClass CSS class to apply to the dropdown list element (defaults to '')
15062      */
15063     listClass: '',
15064     /**
15065      * @cfg {String} selectedClass CSS class to apply to the selected item in the dropdown list (defaults to 'x-combo-selected')
15066      */
15067     selectedClass: 'active',
15068     
15069     /**
15070      * @cfg {Boolean/String} shadow True or "sides" for the default effect, "frame" for 4-way shadow, and "drop" for bottom-right
15071      */
15072     shadow:'sides',
15073     /**
15074      * @cfg {String} listAlign A valid anchor position value. See {@link Roo.Element#alignTo} for details on supported
15075      * anchor positions (defaults to 'tl-bl')
15076      */
15077     listAlign: 'tl-bl?',
15078     /**
15079      * @cfg {Number} maxHeight The maximum height in pixels of the dropdown list before scrollbars are shown (defaults to 300)
15080      */
15081     maxHeight: 300,
15082     /**
15083      * @cfg {String} triggerAction The action to execute when the trigger field is activated.  Use 'all' to run the
15084      * query specified by the allQuery config option (defaults to 'query')
15085      */
15086     triggerAction: 'query',
15087     /**
15088      * @cfg {Number} minChars The minimum number of characters the user must type before autocomplete and typeahead activate
15089      * (defaults to 4, does not apply if editable = false)
15090      */
15091     minChars : 4,
15092     /**
15093      * @cfg {Boolean} typeAhead True to populate and autoselect the remainder of the text being typed after a configurable
15094      * delay (typeAheadDelay) if it matches a known value (defaults to false)
15095      */
15096     typeAhead: false,
15097     /**
15098      * @cfg {Number} queryDelay The length of time in milliseconds to delay between the start of typing and sending the
15099      * query to filter the dropdown list (defaults to 500 if mode = 'remote' or 10 if mode = 'local')
15100      */
15101     queryDelay: 500,
15102     /**
15103      * @cfg {Number} pageSize If greater than 0, a paging toolbar is displayed in the footer of the dropdown list and the
15104      * filter queries will execute with page start and limit parameters.  Only applies when mode = 'remote' (defaults to 0)
15105      */
15106     pageSize: 0,
15107     /**
15108      * @cfg {Boolean} selectOnFocus True to select any existing text in the field immediately on focus.  Only applies
15109      * when editable = true (defaults to false)
15110      */
15111     selectOnFocus:false,
15112     /**
15113      * @cfg {String} queryParam Name of the query as it will be passed on the querystring (defaults to 'query')
15114      */
15115     queryParam: 'query',
15116     /**
15117      * @cfg {String} loadingText The text to display in the dropdown list while data is loading.  Only applies
15118      * when mode = 'remote' (defaults to 'Loading...')
15119      */
15120     loadingText: 'Loading...',
15121     /**
15122      * @cfg {Boolean} resizable True to add a resize handle to the bottom of the dropdown list (defaults to false)
15123      */
15124     resizable: false,
15125     /**
15126      * @cfg {Number} handleHeight The height in pixels of the dropdown list resize handle if resizable = true (defaults to 8)
15127      */
15128     handleHeight : 8,
15129     /**
15130      * @cfg {Boolean} editable False to prevent the user from typing text directly into the field, just like a
15131      * traditional select (defaults to true)
15132      */
15133     editable: true,
15134     /**
15135      * @cfg {String} allQuery The text query to send to the server to return all records for the list with no filtering (defaults to '')
15136      */
15137     allQuery: '',
15138     /**
15139      * @cfg {String} mode Set to 'local' if the ComboBox loads local data (defaults to 'remote' which loads from the server)
15140      */
15141     mode: 'remote',
15142     /**
15143      * @cfg {Number} minListWidth The minimum width of the dropdown list in pixels (defaults to 70, will be ignored if
15144      * listWidth has a higher value)
15145      */
15146     minListWidth : 70,
15147     /**
15148      * @cfg {Boolean} forceSelection True to restrict the selected value to one of the values in the list, false to
15149      * allow the user to set arbitrary text into the field (defaults to false)
15150      */
15151     forceSelection:false,
15152     /**
15153      * @cfg {Number} typeAheadDelay The length of time in milliseconds to wait until the typeahead text is displayed
15154      * if typeAhead = true (defaults to 250)
15155      */
15156     typeAheadDelay : 250,
15157     /**
15158      * @cfg {String} valueNotFoundText When using a name/value combo, if the value passed to setValue is not found in
15159      * the store, valueNotFoundText will be displayed as the field text if defined (defaults to undefined)
15160      */
15161     valueNotFoundText : undefined,
15162     /**
15163      * @cfg {Boolean} blockFocus Prevents all focus calls, so it can work with things like HTML edtor bar
15164      */
15165     blockFocus : false,
15166     
15167     /**
15168      * @cfg {Boolean} disableClear Disable showing of clear button.
15169      */
15170     disableClear : false,
15171     /**
15172      * @cfg {Boolean} alwaysQuery  Disable caching of results, and always send query
15173      */
15174     alwaysQuery : false,
15175     
15176     /**
15177      * @cfg {Boolean} multiple  (true|false) ComboBobArray, default false
15178      */
15179     multiple : false,
15180     
15181     /**
15182      * @cfg {String} invalidClass DEPRICATED - uses BS4 is-valid now
15183      */
15184     invalidClass : "has-warning",
15185     
15186     /**
15187      * @cfg {String} validClass DEPRICATED - uses BS4 is-valid now
15188      */
15189     validClass : "has-success",
15190     
15191     /**
15192      * @cfg {Boolean} specialFilter (true|false) special filter default false
15193      */
15194     specialFilter : false,
15195     
15196     /**
15197      * @cfg {Boolean} mobileTouchView (true|false) show mobile touch view when using a mobile default true
15198      */
15199     mobileTouchView : true,
15200     
15201     /**
15202      * @cfg {Boolean} useNativeIOS (true|false) render it as classic select for ios, not support dynamic load data (default false)
15203      */
15204     useNativeIOS : false,
15205     
15206     /**
15207      * @cfg {Boolean} mobile_restrict_height (true|false) restrict height for touch view
15208      */
15209     mobile_restrict_height : false,
15210     
15211     ios_options : false,
15212     
15213     //private
15214     addicon : false,
15215     editicon: false,
15216     
15217     page: 0,
15218     hasQuery: false,
15219     append: false,
15220     loadNext: false,
15221     autoFocus : true,
15222     tickable : false,
15223     btnPosition : 'right',
15224     triggerList : true,
15225     showToggleBtn : true,
15226     animate : true,
15227     emptyResultText: 'Empty',
15228     triggerText : 'Select',
15229     emptyTitle : '',
15230     width : false,
15231     
15232     // element that contains real text value.. (when hidden is used..)
15233     
15234     getAutoCreate : function()
15235     {   
15236         var cfg = false;
15237         //render
15238         /*
15239          * Render classic select for iso
15240          */
15241         
15242         if(Roo.isIOS && this.useNativeIOS){
15243             cfg = this.getAutoCreateNativeIOS();
15244             return cfg;
15245         }
15246         
15247         /*
15248          * Touch Devices
15249          */
15250         
15251         if(Roo.isTouch && this.mobileTouchView){
15252             cfg = this.getAutoCreateTouchView();
15253             return cfg;;
15254         }
15255         
15256         /*
15257          *  Normal ComboBox
15258          */
15259         if(!this.tickable){
15260             cfg = Roo.bootstrap.ComboBox.superclass.getAutoCreate.call(this);
15261             return cfg;
15262         }
15263         
15264         /*
15265          *  ComboBox with tickable selections
15266          */
15267              
15268         var align = this.labelAlign || this.parentLabelAlign();
15269         
15270         cfg = {
15271             cls : 'form-group roo-combobox-tickable' //input-group
15272         };
15273         
15274         var btn_text_select = '';
15275         var btn_text_done = '';
15276         var btn_text_cancel = '';
15277         
15278         if (this.btn_text_show) {
15279             btn_text_select = 'Select';
15280             btn_text_done = 'Done';
15281             btn_text_cancel = 'Cancel'; 
15282         }
15283         
15284         var buttons = {
15285             tag : 'div',
15286             cls : 'tickable-buttons',
15287             cn : [
15288                 {
15289                     tag : 'button',
15290                     type : 'button',
15291                     cls : 'btn btn-link btn-edit pull-' + this.btnPosition,
15292                     //html : this.triggerText
15293                     html: btn_text_select
15294                 },
15295                 {
15296                     tag : 'button',
15297                     type : 'button',
15298                     name : 'ok',
15299                     cls : 'btn btn-link btn-ok pull-' + this.btnPosition,
15300                     //html : 'Done'
15301                     html: btn_text_done
15302                 },
15303                 {
15304                     tag : 'button',
15305                     type : 'button',
15306                     name : 'cancel',
15307                     cls : 'btn btn-link btn-cancel pull-' + this.btnPosition,
15308                     //html : 'Cancel'
15309                     html: btn_text_cancel
15310                 }
15311             ]
15312         };
15313         
15314         if(this.editable){
15315             buttons.cn.unshift({
15316                 tag: 'input',
15317                 cls: 'roo-select2-search-field-input'
15318             });
15319         }
15320         
15321         var _this = this;
15322         
15323         Roo.each(buttons.cn, function(c){
15324             if (_this.size) {
15325                 c.cls += ' btn-' + _this.size;
15326             }
15327
15328             if (_this.disabled) {
15329                 c.disabled = true;
15330             }
15331         });
15332         
15333         var box = {
15334             tag: 'div',
15335             style : 'display: contents',
15336             cn: [
15337                 {
15338                     tag: 'input',
15339                     type : 'hidden',
15340                     cls: 'form-hidden-field'
15341                 },
15342                 {
15343                     tag: 'ul',
15344                     cls: 'roo-select2-choices',
15345                     cn:[
15346                         {
15347                             tag: 'li',
15348                             cls: 'roo-select2-search-field',
15349                             cn: [
15350                                 buttons
15351                             ]
15352                         }
15353                     ]
15354                 }
15355             ]
15356         };
15357         
15358         var combobox = {
15359             cls: 'roo-select2-container input-group roo-select2-container-multi',
15360             cn: [
15361                 
15362                 box
15363 //                {
15364 //                    tag: 'ul',
15365 //                    cls: 'typeahead typeahead-long dropdown-menu',
15366 //                    style: 'display:none; max-height:' + this.maxHeight + 'px;'
15367 //                }
15368             ]
15369         };
15370         
15371         if(this.hasFeedback && !this.allowBlank){
15372             
15373             var feedback = {
15374                 tag: 'span',
15375                 cls: 'glyphicon form-control-feedback'
15376             };
15377
15378             combobox.cn.push(feedback);
15379         }
15380         
15381         
15382         
15383         var indicator = {
15384             tag : 'i',
15385             cls : 'roo-required-indicator ' + (this.indicatorpos == 'right'  ? 'right' : 'left') +'-indicator text-danger fa fa-lg fa-star',
15386             tooltip : 'This field is required'
15387         };
15388         if (Roo.bootstrap.version == 4) {
15389             indicator = {
15390                 tag : 'i',
15391                 style : 'display:none'
15392             };
15393         }
15394         if (align ==='left' && this.fieldLabel.length) {
15395             
15396             cfg.cls += ' roo-form-group-label-left'  + (Roo.bootstrap.version == 4 ? ' row' : '');
15397             
15398             cfg.cn = [
15399                 indicator,
15400                 {
15401                     tag: 'label',
15402                     'for' :  id,
15403                     cls : 'control-label col-form-label',
15404                     html : this.fieldLabel
15405
15406                 },
15407                 {
15408                     cls : "", 
15409                     cn: [
15410                         combobox
15411                     ]
15412                 }
15413
15414             ];
15415             
15416             var labelCfg = cfg.cn[1];
15417             var contentCfg = cfg.cn[2];
15418             
15419
15420             if(this.indicatorpos == 'right'){
15421                 
15422                 cfg.cn = [
15423                     {
15424                         tag: 'label',
15425                         'for' :  id,
15426                         cls : 'control-label col-form-label',
15427                         cn : [
15428                             {
15429                                 tag : 'span',
15430                                 html : this.fieldLabel
15431                             },
15432                             indicator
15433                         ]
15434                     },
15435                     {
15436                         cls : "",
15437                         cn: [
15438                             combobox
15439                         ]
15440                     }
15441
15442                 ];
15443                 
15444                 
15445                 
15446                 labelCfg = cfg.cn[0];
15447                 contentCfg = cfg.cn[1];
15448             
15449             }
15450             
15451             if(this.labelWidth > 12){
15452                 labelCfg.style = "width: " + this.labelWidth + 'px';
15453             }
15454             if(this.width * 1 > 0){
15455                 contentCfg.style = "width: " + this.width + 'px';
15456             }
15457             if(this.labelWidth < 13 && this.labelmd == 0){
15458                 this.labelmd = this.labelWidth;
15459             }
15460             
15461             if(this.labellg > 0){
15462                 labelCfg.cls += ' col-lg-' + this.labellg;
15463                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
15464             }
15465             
15466             if(this.labelmd > 0){
15467                 labelCfg.cls += ' col-md-' + this.labelmd;
15468                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
15469             }
15470             
15471             if(this.labelsm > 0){
15472                 labelCfg.cls += ' col-sm-' + this.labelsm;
15473                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
15474             }
15475             
15476             if(this.labelxs > 0){
15477                 labelCfg.cls += ' col-xs-' + this.labelxs;
15478                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
15479             }
15480                 
15481                 
15482         } else if ( this.fieldLabel.length) {
15483 //                Roo.log(" label");
15484                  cfg.cn = [
15485                    indicator,
15486                     {
15487                         tag: 'label',
15488                         //cls : 'input-group-addon',
15489                         html : this.fieldLabel
15490                     },
15491                     combobox
15492                 ];
15493                 
15494                 if(this.indicatorpos == 'right'){
15495                     cfg.cn = [
15496                         {
15497                             tag: 'label',
15498                             //cls : 'input-group-addon',
15499                             html : this.fieldLabel
15500                         },
15501                         indicator,
15502                         combobox
15503                     ];
15504                     
15505                 }
15506
15507         } else {
15508             
15509 //                Roo.log(" no label && no align");
15510                 cfg = combobox
15511                      
15512                 
15513         }
15514          
15515         var settings=this;
15516         ['xs','sm','md','lg'].map(function(size){
15517             if (settings[size]) {
15518                 cfg.cls += ' col-' + size + '-' + settings[size];
15519             }
15520         });
15521         
15522         return cfg;
15523         
15524     },
15525     
15526     _initEventsCalled : false,
15527     
15528     // private
15529     initEvents: function()
15530     {   
15531         if (this._initEventsCalled) { // as we call render... prevent looping...
15532             return;
15533         }
15534         this._initEventsCalled = true;
15535         
15536         if (!this.store) {
15537             throw "can not find store for combo";
15538         }
15539         
15540         this.indicator = this.indicatorEl();
15541         
15542         this.store = Roo.factory(this.store, Roo.data);
15543         this.store.parent = this;
15544         
15545         // if we are building from html. then this element is so complex, that we can not really
15546         // use the rendered HTML.
15547         // so we have to trash and replace the previous code.
15548         if (Roo.XComponent.build_from_html) {
15549             // remove this element....
15550             var e = this.el.dom, k=0;
15551             while (e ) { e = e.previousSibling;  ++k;}
15552
15553             this.el.remove();
15554             
15555             this.el=false;
15556             this.rendered = false;
15557             
15558             this.render(this.parent().getChildContainer(true), k);
15559         }
15560         
15561         if(Roo.isIOS && this.useNativeIOS){
15562             this.initIOSView();
15563             return;
15564         }
15565         
15566         /*
15567          * Touch Devices
15568          */
15569         
15570         if(Roo.isTouch && this.mobileTouchView){
15571             this.initTouchView();
15572             return;
15573         }
15574         
15575         if(this.tickable){
15576             this.initTickableEvents();
15577             return;
15578         }
15579         
15580         Roo.bootstrap.ComboBox.superclass.initEvents.call(this);
15581         
15582         if(this.hiddenName){
15583             
15584             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15585             
15586             this.hiddenField.dom.value =
15587                 this.hiddenValue !== undefined ? this.hiddenValue :
15588                 this.value !== undefined ? this.value : '';
15589
15590             // prevent input submission
15591             this.el.dom.removeAttribute('name');
15592             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15593              
15594              
15595         }
15596         //if(Roo.isGecko){
15597         //    this.el.dom.setAttribute('autocomplete', 'off');
15598         //}
15599         
15600         var cls = 'x-combo-list';
15601         
15602         //this.list = new Roo.Layer({
15603         //    shadow: this.shadow, cls: [cls, this.listClass].join(' '), constrain:false
15604         //});
15605         
15606         var _this = this;
15607         
15608         (function(){
15609             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15610             _this.list.setWidth(lw);
15611         }).defer(100);
15612         
15613         this.list.on('mouseover', this.onViewOver, this);
15614         this.list.on('mousemove', this.onViewMove, this);
15615         this.list.on('scroll', this.onViewScroll, this);
15616         
15617         /*
15618         this.list.swallowEvent('mousewheel');
15619         this.assetHeight = 0;
15620
15621         if(this.title){
15622             this.header = this.list.createChild({cls:cls+'-hd', html: this.title});
15623             this.assetHeight += this.header.getHeight();
15624         }
15625
15626         this.innerList = this.list.createChild({cls:cls+'-inner'});
15627         this.innerList.on('mouseover', this.onViewOver, this);
15628         this.innerList.on('mousemove', this.onViewMove, this);
15629         this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15630         
15631         if(this.allowBlank && !this.pageSize && !this.disableClear){
15632             this.footer = this.list.createChild({cls:cls+'-ft'});
15633             this.pageTb = new Roo.Toolbar(this.footer);
15634            
15635         }
15636         if(this.pageSize){
15637             this.footer = this.list.createChild({cls:cls+'-ft'});
15638             this.pageTb = new Roo.PagingToolbar(this.footer, this.store,
15639                     {pageSize: this.pageSize});
15640             
15641         }
15642         
15643         if (this.pageTb && this.allowBlank && !this.disableClear) {
15644             var _this = this;
15645             this.pageTb.add(new Roo.Toolbar.Fill(), {
15646                 cls: 'x-btn-icon x-btn-clear',
15647                 text: '&#160;',
15648                 handler: function()
15649                 {
15650                     _this.collapse();
15651                     _this.clearValue();
15652                     _this.onSelect(false, -1);
15653                 }
15654             });
15655         }
15656         if (this.footer) {
15657             this.assetHeight += this.footer.getHeight();
15658         }
15659         */
15660             
15661         if(!this.tpl){
15662             this.tpl = Roo.bootstrap.version == 4 ?
15663                 '<a class="dropdown-item" href="#">{' + this.displayField + '}</a>' :  // 4 does not need <li> and it get's really confisued.
15664                 '<li><a class="dropdown-item" href="#">{' + this.displayField + '}</a></li>';
15665         }
15666
15667         this.view = new Roo.View(this.list, this.tpl, {
15668             singleSelect:true, store: this.store, selectedClass: this.selectedClass
15669         });
15670         //this.view.wrapEl.setDisplayed(false);
15671         this.view.on('click', this.onViewClick, this);
15672         
15673         
15674         this.store.on('beforeload', this.onBeforeLoad, this);
15675         this.store.on('load', this.onLoad, this);
15676         this.store.on('loadexception', this.onLoadException, this);
15677         /*
15678         if(this.resizable){
15679             this.resizer = new Roo.Resizable(this.list,  {
15680                pinned:true, handles:'se'
15681             });
15682             this.resizer.on('resize', function(r, w, h){
15683                 this.maxHeight = h-this.handleHeight-this.list.getFrameWidth('tb')-this.assetHeight;
15684                 this.listWidth = w;
15685                 this.innerList.setWidth(w - this.list.getFrameWidth('lr'));
15686                 this.restrictHeight();
15687             }, this);
15688             this[this.pageSize?'footer':'innerList'].setStyle('margin-bottom', this.handleHeight+'px');
15689         }
15690         */
15691         if(!this.editable){
15692             this.editable = true;
15693             this.setEditable(false);
15694         }
15695         
15696         /*
15697         
15698         if (typeof(this.events.add.listeners) != 'undefined') {
15699             
15700             this.addicon = this.wrap.createChild(
15701                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-add' });  
15702        
15703             this.addicon.on('click', function(e) {
15704                 this.fireEvent('add', this);
15705             }, this);
15706         }
15707         if (typeof(this.events.edit.listeners) != 'undefined') {
15708             
15709             this.editicon = this.wrap.createChild(
15710                 {tag: 'img', src: Roo.BLANK_IMAGE_URL, cls: 'x-form-combo-edit' });  
15711             if (this.addicon) {
15712                 this.editicon.setStyle('margin-left', '40px');
15713             }
15714             this.editicon.on('click', function(e) {
15715                 
15716                 // we fire even  if inothing is selected..
15717                 this.fireEvent('edit', this, this.lastData );
15718                 
15719             }, this);
15720         }
15721         */
15722         
15723         this.keyNav = new Roo.KeyNav(this.inputEl(), {
15724             "up" : function(e){
15725                 this.inKeyMode = true;
15726                 this.selectPrev();
15727             },
15728
15729             "down" : function(e){
15730                 if(!this.isExpanded()){
15731                     this.onTriggerClick();
15732                 }else{
15733                     this.inKeyMode = true;
15734                     this.selectNext();
15735                 }
15736             },
15737
15738             "enter" : function(e){
15739 //                this.onViewClick();
15740                 //return true;
15741                 this.collapse();
15742                 
15743                 if(this.fireEvent("specialkey", this, e)){
15744                     this.onViewClick(false);
15745                 }
15746                 
15747                 return true;
15748             },
15749
15750             "esc" : function(e){
15751                 this.collapse();
15752             },
15753
15754             "tab" : function(e){
15755                 this.collapse();
15756                 
15757                 if(this.fireEvent("specialkey", this, e)){
15758                     this.onViewClick(false);
15759                 }
15760                 
15761                 return true;
15762             },
15763
15764             scope : this,
15765
15766             doRelay : function(foo, bar, hname){
15767                 if(hname == 'down' || this.scope.isExpanded()){
15768                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15769                 }
15770                 return true;
15771             },
15772
15773             forceKeyDown: true
15774         });
15775         
15776         
15777         this.queryDelay = Math.max(this.queryDelay || 10,
15778                 this.mode == 'local' ? 10 : 250);
15779         
15780         
15781         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15782         
15783         if(this.typeAhead){
15784             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15785         }
15786         if(this.editable !== false){
15787             this.inputEl().on("keyup", this.onKeyUp, this);
15788         }
15789         if(this.forceSelection){
15790             this.inputEl().on('blur', this.doForce, this);
15791         }
15792         
15793         if(this.multiple){
15794             this.choices = this.el.select('ul.roo-select2-choices', true).first();
15795             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15796         }
15797     },
15798     
15799     initTickableEvents: function()
15800     {   
15801         this.createList();
15802         
15803         if(this.hiddenName){
15804             
15805             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
15806             
15807             this.hiddenField.dom.value =
15808                 this.hiddenValue !== undefined ? this.hiddenValue :
15809                 this.value !== undefined ? this.value : '';
15810
15811             // prevent input submission
15812             this.el.dom.removeAttribute('name');
15813             this.hiddenField.dom.setAttribute('name', this.hiddenName);
15814              
15815              
15816         }
15817         
15818 //        this.list = this.el.select('ul.dropdown-menu',true).first();
15819         
15820         this.choices = this.el.select('ul.roo-select2-choices', true).first();
15821         this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
15822         if(this.triggerList){
15823             this.searchField.on("click", this.onSearchFieldClick, this, {preventDefault:true});
15824         }
15825          
15826         this.trigger = this.el.select('.tickable-buttons > .btn-edit', true).first();
15827         this.trigger.on("click", this.onTickableTriggerClick, this, {preventDefault:true});
15828         
15829         this.okBtn = this.el.select('.tickable-buttons > .btn-ok', true).first();
15830         this.cancelBtn = this.el.select('.tickable-buttons > .btn-cancel', true).first();
15831         
15832         this.okBtn.on('click', this.onTickableFooterButtonClick, this, this.okBtn);
15833         this.cancelBtn.on('click', this.onTickableFooterButtonClick, this, this.cancelBtn);
15834         
15835         this.trigger.setVisibilityMode(Roo.Element.DISPLAY);
15836         this.okBtn.setVisibilityMode(Roo.Element.DISPLAY);
15837         this.cancelBtn.setVisibilityMode(Roo.Element.DISPLAY);
15838         
15839         this.okBtn.hide();
15840         this.cancelBtn.hide();
15841         
15842         var _this = this;
15843         
15844         (function(){
15845             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
15846             _this.list.setWidth(lw);
15847         }).defer(100);
15848         
15849         this.list.on('mouseover', this.onViewOver, this);
15850         this.list.on('mousemove', this.onViewMove, this);
15851         
15852         this.list.on('scroll', this.onViewScroll, this);
15853         
15854         if(!this.tpl){
15855             this.tpl = '<li class="roo-select2-result"><div class="checkbox"><input id="{roo-id}"' + 
15856                 'type="checkbox" {roo-data-checked}><label for="{roo-id}"><b>{' + this.displayField + '}</b></label></div></li>';
15857         }
15858
15859         this.view = new Roo.View(this.list, this.tpl, {
15860             singleSelect:true,
15861             tickable:true,
15862             parent:this,
15863             store: this.store,
15864             selectedClass: this.selectedClass
15865         });
15866         
15867         //this.view.wrapEl.setDisplayed(false);
15868         this.view.on('click', this.onViewClick, this);
15869         
15870         
15871         
15872         this.store.on('beforeload', this.onBeforeLoad, this);
15873         this.store.on('load', this.onLoad, this);
15874         this.store.on('loadexception', this.onLoadException, this);
15875         
15876         if(this.editable){
15877             this.keyNav = new Roo.KeyNav(this.tickableInputEl(), {
15878                 "up" : function(e){
15879                     this.inKeyMode = true;
15880                     this.selectPrev();
15881                 },
15882
15883                 "down" : function(e){
15884                     this.inKeyMode = true;
15885                     this.selectNext();
15886                 },
15887
15888                 "enter" : function(e){
15889                     if(this.fireEvent("specialkey", this, e)){
15890                         this.onViewClick(false);
15891                     }
15892                     
15893                     return true;
15894                 },
15895
15896                 "esc" : function(e){
15897                     this.onTickableFooterButtonClick(e, false, false);
15898                 },
15899
15900                 "tab" : function(e){
15901                     this.fireEvent("specialkey", this, e);
15902                     
15903                     this.onTickableFooterButtonClick(e, false, false);
15904                     
15905                     return true;
15906                 },
15907
15908                 scope : this,
15909
15910                 doRelay : function(e, fn, key){
15911                     if(this.scope.isExpanded()){
15912                        return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
15913                     }
15914                     return true;
15915                 },
15916
15917                 forceKeyDown: true
15918             });
15919         }
15920         
15921         this.queryDelay = Math.max(this.queryDelay || 10,
15922                 this.mode == 'local' ? 10 : 250);
15923         
15924         
15925         this.dqTask = new Roo.util.DelayedTask(this.initQuery, this);
15926         
15927         if(this.typeAhead){
15928             this.taTask = new Roo.util.DelayedTask(this.onTypeAhead, this);
15929         }
15930         
15931         if(this.editable !== false){
15932             this.tickableInputEl().on("keyup", this.onKeyUp, this);
15933         }
15934         
15935         this.indicator = this.indicatorEl();
15936         
15937         if(this.indicator){
15938             this.indicator.setVisibilityMode(Roo.Element.DISPLAY);
15939             this.indicator.hide();
15940         }
15941         
15942     },
15943
15944     onDestroy : function(){
15945         if(this.view){
15946             this.view.setStore(null);
15947             this.view.el.removeAllListeners();
15948             this.view.el.remove();
15949             this.view.purgeListeners();
15950         }
15951         if(this.list){
15952             this.list.dom.innerHTML  = '';
15953         }
15954         
15955         if(this.store){
15956             this.store.un('beforeload', this.onBeforeLoad, this);
15957             this.store.un('load', this.onLoad, this);
15958             this.store.un('loadexception', this.onLoadException, this);
15959         }
15960         Roo.bootstrap.ComboBox.superclass.onDestroy.call(this);
15961     },
15962
15963     // private
15964     fireKey : function(e){
15965         if(e.isNavKeyPress() && !this.list.isVisible()){
15966             this.fireEvent("specialkey", this, e);
15967         }
15968     },
15969
15970     // private
15971     onResize: function(w, h)
15972     {
15973         
15974         
15975 //        Roo.bootstrap.ComboBox.superclass.onResize.apply(this, arguments);
15976 //        
15977 //        if(typeof w != 'number'){
15978 //            // we do not handle it!?!?
15979 //            return;
15980 //        }
15981 //        var tw = this.trigger.getWidth();
15982 //       // tw += this.addicon ? this.addicon.getWidth() : 0;
15983 //       // tw += this.editicon ? this.editicon.getWidth() : 0;
15984 //        var x = w - tw;
15985 //        this.inputEl().setWidth( this.adjustWidth('input', x));
15986 //            
15987 //        //this.trigger.setStyle('left', x+'px');
15988 //        
15989 //        if(this.list && this.listWidth === undefined){
15990 //            var lw = Math.max(x + this.trigger.getWidth(), this.minListWidth);
15991 //            this.list.setWidth(lw);
15992 //            this.innerList.setWidth(lw - this.list.getFrameWidth('lr'));
15993 //        }
15994         
15995     
15996         
15997     },
15998
15999     /**
16000      * Allow or prevent the user from directly editing the field text.  If false is passed,
16001      * the user will only be able to select from the items defined in the dropdown list.  This method
16002      * is the runtime equivalent of setting the 'editable' config option at config time.
16003      * @param {Boolean} value True to allow the user to directly edit the field text
16004      */
16005     setEditable : function(value){
16006         if(value == this.editable){
16007             return;
16008         }
16009         this.editable = value;
16010         if(!value){
16011             this.inputEl().dom.setAttribute('readOnly', true);
16012             this.inputEl().on('mousedown', this.onTriggerClick,  this);
16013             this.inputEl().addClass('x-combo-noedit');
16014         }else{
16015             this.inputEl().dom.setAttribute('readOnly', false);
16016             this.inputEl().un('mousedown', this.onTriggerClick,  this);
16017             this.inputEl().removeClass('x-combo-noedit');
16018         }
16019     },
16020
16021     // private
16022     
16023     onBeforeLoad : function(combo,opts){
16024         if(!this.hasFocus){
16025             return;
16026         }
16027          if (!opts.add) {
16028             this.list.dom.innerHTML = '<li class="loading-indicator">'+(this.loadingText||'loading')+'</li>' ;
16029          }
16030         this.restrictHeight();
16031         this.selectedIndex = -1;
16032     },
16033
16034     // private
16035     onLoad : function(){
16036         
16037         this.hasQuery = false;
16038         
16039         if(!this.hasFocus){
16040             return;
16041         }
16042         
16043         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16044             this.loading.hide();
16045         }
16046         
16047         if(this.store.getCount() > 0){
16048             
16049             this.expand();
16050             this.restrictHeight();
16051             if(this.lastQuery == this.allQuery){
16052                 if(this.editable && !this.tickable){
16053                     this.inputEl().dom.select();
16054                 }
16055                 
16056                 if(
16057                     !this.selectByValue(this.value, true) &&
16058                     this.autoFocus && 
16059                     (
16060                         !this.store.lastOptions ||
16061                         typeof(this.store.lastOptions.add) == 'undefined' || 
16062                         this.store.lastOptions.add != true
16063                     )
16064                 ){
16065                     this.select(0, true);
16066                 }
16067             }else{
16068                 if(this.autoFocus){
16069                     this.selectNext();
16070                 }
16071                 if(this.typeAhead && this.lastKey != Roo.EventObject.BACKSPACE && this.lastKey != Roo.EventObject.DELETE){
16072                     this.taTask.delay(this.typeAheadDelay);
16073                 }
16074             }
16075         }else{
16076             this.onEmptyResults();
16077         }
16078         
16079         //this.el.focus();
16080     },
16081     // private
16082     onLoadException : function()
16083     {
16084         this.hasQuery = false;
16085         
16086         if(typeof(this.loading) !== 'undefined' && this.loading !== null){
16087             this.loading.hide();
16088         }
16089         
16090         if(this.tickable && this.editable){
16091             return;
16092         }
16093         
16094         this.collapse();
16095         // only causes errors at present
16096         //Roo.log(this.store.reader.jsonData);
16097         //if (this.store && typeof(this.store.reader.jsonData.errorMsg) != 'undefined') {
16098             // fixme
16099             //Roo.MessageBox.alert("Error loading",this.store.reader.jsonData.errorMsg);
16100         //}
16101         
16102         
16103     },
16104     // private
16105     onTypeAhead : function(){
16106         if(this.store.getCount() > 0){
16107             var r = this.store.getAt(0);
16108             var newValue = r.data[this.displayField];
16109             var len = newValue.length;
16110             var selStart = this.getRawValue().length;
16111             
16112             if(selStart != len){
16113                 this.setRawValue(newValue);
16114                 this.selectText(selStart, newValue.length);
16115             }
16116         }
16117     },
16118
16119     // private
16120     onSelect : function(record, index){
16121         
16122         if(this.fireEvent('beforeselect', this, record, index) !== false){
16123         
16124             this.setFromData(index > -1 ? record.data : false);
16125             
16126             this.collapse();
16127             this.fireEvent('select', this, record, index);
16128         }
16129     },
16130
16131     /**
16132      * Returns the currently selected field value or empty string if no value is set.
16133      * @return {String} value The selected value
16134      */
16135     getValue : function()
16136     {
16137         if(Roo.isIOS && this.useNativeIOS){
16138             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.valueField];
16139         }
16140         
16141         if(this.multiple){
16142             return (this.hiddenField) ? this.hiddenField.dom.value : this.value;
16143         }
16144         
16145         if(this.valueField){
16146             return typeof this.value != 'undefined' ? this.value : '';
16147         }else{
16148             return Roo.bootstrap.ComboBox.superclass.getValue.call(this);
16149         }
16150     },
16151     
16152     getRawValue : function()
16153     {
16154         if(Roo.isIOS && this.useNativeIOS){
16155             return this.ios_options[this.inputEl().dom.selectedIndex].data[this.displayField];
16156         }
16157         
16158         var v = this.inputEl().getValue();
16159         
16160         return v;
16161     },
16162
16163     /**
16164      * Clears any text/value currently set in the field
16165      */
16166     clearValue : function(){
16167         
16168         if(this.hiddenField){
16169             this.hiddenField.dom.value = '';
16170         }
16171         this.value = '';
16172         this.setRawValue('');
16173         this.lastSelectionText = '';
16174         this.lastData = false;
16175         
16176         var close = this.closeTriggerEl();
16177         
16178         if(close){
16179             close.hide();
16180         }
16181         
16182         this.validate();
16183         
16184     },
16185
16186     /**
16187      * Sets the specified value into the field.  If the value finds a match, the corresponding record text
16188      * will be displayed in the field.  If the value does not match the data value of an existing item,
16189      * and the valueNotFoundText config option is defined, it will be displayed as the default field text.
16190      * Otherwise the field will be blank (although the value will still be set).
16191      * @param {String} value The value to match
16192      */
16193     setValue : function(v)
16194     {
16195         if(Roo.isIOS && this.useNativeIOS){
16196             this.setIOSValue(v);
16197             return;
16198         }
16199         
16200         if(this.multiple){
16201             this.syncValue();
16202             return;
16203         }
16204         
16205         var text = v;
16206         if(this.valueField){
16207             var r = this.findRecord(this.valueField, v);
16208             if(r){
16209                 text = r.data[this.displayField];
16210             }else if(this.valueNotFoundText !== undefined){
16211                 text = this.valueNotFoundText;
16212             }
16213         }
16214         this.lastSelectionText = text;
16215         if(this.hiddenField){
16216             this.hiddenField.dom.value = v;
16217         }
16218         Roo.bootstrap.ComboBox.superclass.setValue.call(this, text);
16219         this.value = v;
16220         
16221         var close = this.closeTriggerEl();
16222         
16223         if(close){
16224             (v && (v.length || v * 1 > 0)) ? close.show() : close.hide();
16225         }
16226         
16227         this.validate();
16228     },
16229     /**
16230      * @property {Object} the last set data for the element
16231      */
16232     
16233     lastData : false,
16234     /**
16235      * Sets the value of the field based on a object which is related to the record format for the store.
16236      * @param {Object} value the value to set as. or false on reset?
16237      */
16238     setFromData : function(o){
16239         
16240         if(this.multiple){
16241             this.addItem(o);
16242             return;
16243         }
16244             
16245         var dv = ''; // display value
16246         var vv = ''; // value value..
16247         this.lastData = o;
16248         if (this.displayField) {
16249             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16250         } else {
16251             // this is an error condition!!!
16252             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16253         }
16254         
16255         if(this.valueField){
16256             vv = !o || typeof(o[this.valueField]) == 'undefined' ? dv : o[this.valueField];
16257         }
16258         
16259         var close = this.closeTriggerEl();
16260         
16261         if(close){
16262             if(dv.length || vv * 1 > 0){
16263                 close.show() ;
16264                 this.blockFocus=true;
16265             } else {
16266                 close.hide();
16267             }             
16268         }
16269         
16270         if(this.hiddenField){
16271             this.hiddenField.dom.value = vv;
16272             
16273             this.lastSelectionText = dv;
16274             Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16275             this.value = vv;
16276             return;
16277         }
16278         // no hidden field.. - we store the value in 'value', but still display
16279         // display field!!!!
16280         this.lastSelectionText = dv;
16281         Roo.bootstrap.ComboBox.superclass.setValue.call(this, dv);
16282         this.value = vv;
16283         
16284         
16285         
16286     },
16287     // private
16288     reset : function(){
16289         // overridden so that last data is reset..
16290         
16291         if(this.multiple){
16292             this.clearItem();
16293             return;
16294         }
16295         
16296         this.setValue(this.originalValue);
16297         //this.clearInvalid();
16298         this.lastData = false;
16299         if (this.view) {
16300             this.view.clearSelections();
16301         }
16302         
16303         this.validate();
16304     },
16305     // private
16306     findRecord : function(prop, value){
16307         var record;
16308         if(this.store.getCount() > 0){
16309             this.store.each(function(r){
16310                 if(r.data[prop] == value){
16311                     record = r;
16312                     return false;
16313                 }
16314                 return true;
16315             });
16316         }
16317         return record;
16318     },
16319     
16320     getName: function()
16321     {
16322         // returns hidden if it's set..
16323         if (!this.rendered) {return ''};
16324         return !this.hiddenName && this.inputEl().dom.name  ? this.inputEl().dom.name : (this.hiddenName || '');
16325         
16326     },
16327     // private
16328     onViewMove : function(e, t){
16329         this.inKeyMode = false;
16330     },
16331
16332     // private
16333     onViewOver : function(e, t){
16334         if(this.inKeyMode){ // prevent key nav and mouse over conflicts
16335             return;
16336         }
16337         var item = this.view.findItemFromChild(t);
16338         
16339         if(item){
16340             var index = this.view.indexOf(item);
16341             this.select(index, false);
16342         }
16343     },
16344
16345     // private
16346     onViewClick : function(view, doFocus, el, e)
16347     {
16348         var index = this.view.getSelectedIndexes()[0];
16349         
16350         var r = this.store.getAt(index);
16351         
16352         if(this.tickable){
16353             
16354             if(typeof(e) != 'undefined' && e.getTarget().nodeName.toLowerCase() != 'input'){
16355                 return;
16356             }
16357             
16358             var rm = false;
16359             var _this = this;
16360             
16361             Roo.each(this.tickItems, function(v,k){
16362                 
16363                 if(typeof(v) != 'undefined' && v[_this.valueField] == r.data[_this.valueField]){
16364                     Roo.log(v);
16365                     _this.tickItems.splice(k, 1);
16366                     
16367                     if(typeof(e) == 'undefined' && view == false){
16368                         Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = false;
16369                     }
16370                     
16371                     rm = true;
16372                     return;
16373                 }
16374             });
16375             
16376             if(rm){
16377                 return;
16378             }
16379             
16380             if(this.fireEvent('tick', this, r, index, Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked) !== false){
16381                 this.tickItems.push(r.data);
16382             }
16383             
16384             if(typeof(e) == 'undefined' && view == false){
16385                 Roo.get(_this.view.getNodes(index, index)[0]).select('input', true).first().dom.checked = true;
16386             }
16387                     
16388             return;
16389         }
16390         
16391         if(r){
16392             this.onSelect(r, index);
16393         }
16394         if(doFocus !== false && !this.blockFocus){
16395             this.inputEl().focus();
16396         }
16397     },
16398
16399     // private
16400     restrictHeight : function(){
16401         //this.innerList.dom.style.height = '';
16402         //var inner = this.innerList.dom;
16403         //var h = Math.max(inner.clientHeight, inner.offsetHeight, inner.scrollHeight);
16404         //this.innerList.setHeight(h < this.maxHeight ? 'auto' : this.maxHeight);
16405         //this.list.beginUpdate();
16406         //this.list.setHeight(this.innerList.getHeight()+this.list.getFrameWidth('tb')+(this.resizable?this.handleHeight:0)+this.assetHeight);
16407         this.list.alignTo(this.inputEl(), this.listAlign);
16408         this.list.alignTo(this.inputEl(), this.listAlign);
16409         //this.list.endUpdate();
16410     },
16411
16412     // private
16413     onEmptyResults : function(){
16414         
16415         if(this.tickable && this.editable){
16416             this.hasFocus = false;
16417             this.restrictHeight();
16418             return;
16419         }
16420         
16421         this.collapse();
16422     },
16423
16424     /**
16425      * Returns true if the dropdown list is expanded, else false.
16426      */
16427     isExpanded : function(){
16428         return this.list.isVisible();
16429     },
16430
16431     /**
16432      * Select an item in the dropdown list by its data value. This function does NOT cause the select event to fire.
16433      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16434      * @param {String} value The data value of the item to select
16435      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16436      * selected item if it is not currently in view (defaults to true)
16437      * @return {Boolean} True if the value matched an item in the list, else false
16438      */
16439     selectByValue : function(v, scrollIntoView){
16440         if(v !== undefined && v !== null){
16441             var r = this.findRecord(this.valueField || this.displayField, v);
16442             if(r){
16443                 this.select(this.store.indexOf(r), scrollIntoView);
16444                 return true;
16445             }
16446         }
16447         return false;
16448     },
16449
16450     /**
16451      * Select an item in the dropdown list by its numeric index in the list. This function does NOT cause the select event to fire.
16452      * The store must be loaded and the list expanded for this function to work, otherwise use setValue.
16453      * @param {Number} index The zero-based index of the list item to select
16454      * @param {Boolean} scrollIntoView False to prevent the dropdown list from autoscrolling to display the
16455      * selected item if it is not currently in view (defaults to true)
16456      */
16457     select : function(index, scrollIntoView){
16458         this.selectedIndex = index;
16459         this.view.select(index);
16460         if(scrollIntoView !== false){
16461             var el = this.view.getNode(index);
16462             /*
16463              * el && !this.multiple && !this.tickable // not sure why we disable multiple before..
16464              */
16465             if(el){
16466                 this.list.scrollChildIntoView(el, false);
16467             }
16468         }
16469     },
16470
16471     // private
16472     selectNext : function(){
16473         var ct = this.store.getCount();
16474         if(ct > 0){
16475             if(this.selectedIndex == -1){
16476                 this.select(0);
16477             }else if(this.selectedIndex < ct-1){
16478                 this.select(this.selectedIndex+1);
16479             }
16480         }
16481     },
16482
16483     // private
16484     selectPrev : function(){
16485         var ct = this.store.getCount();
16486         if(ct > 0){
16487             if(this.selectedIndex == -1){
16488                 this.select(0);
16489             }else if(this.selectedIndex != 0){
16490                 this.select(this.selectedIndex-1);
16491             }
16492         }
16493     },
16494
16495     // private
16496     onKeyUp : function(e){
16497         if(this.editable !== false && !e.isSpecialKey()){
16498             this.lastKey = e.getKey();
16499             this.dqTask.delay(this.queryDelay);
16500         }
16501     },
16502
16503     // private
16504     validateBlur : function(){
16505         return !this.list || !this.list.isVisible();   
16506     },
16507
16508     // private
16509     initQuery : function(){
16510         
16511         var v = this.getRawValue();
16512         
16513         if(this.tickable && this.editable){
16514             v = this.tickableInputEl().getValue();
16515         }
16516         
16517         this.doQuery(v);
16518     },
16519
16520     // private
16521     doForce : function(){
16522         if(this.inputEl().dom.value.length > 0){
16523             this.inputEl().dom.value =
16524                 this.lastSelectionText === undefined ? '' : this.lastSelectionText;
16525              
16526         }
16527     },
16528
16529     /**
16530      * Execute a query to filter the dropdown list.  Fires the beforequery event prior to performing the
16531      * query allowing the query action to be canceled if needed.
16532      * @param {String} query The SQL query to execute
16533      * @param {Boolean} forceAll True to force the query to execute even if there are currently fewer characters
16534      * in the field than the minimum specified by the minChars config option.  It also clears any filter previously
16535      * saved in the current store (defaults to false)
16536      */
16537     doQuery : function(q, forceAll){
16538         
16539         if(q === undefined || q === null){
16540             q = '';
16541         }
16542         var qe = {
16543             query: q,
16544             forceAll: forceAll,
16545             combo: this,
16546             cancel:false
16547         };
16548         if(this.fireEvent('beforequery', qe)===false || qe.cancel){
16549             return false;
16550         }
16551         q = qe.query;
16552         
16553         forceAll = qe.forceAll;
16554         if(forceAll === true || (q.length >= this.minChars)){
16555             
16556             this.hasQuery = true;
16557             
16558             if(this.lastQuery != q || this.alwaysQuery){
16559                 this.lastQuery = q;
16560                 if(this.mode == 'local'){
16561                     this.selectedIndex = -1;
16562                     if(forceAll){
16563                         this.store.clearFilter();
16564                     }else{
16565                         
16566                         if(this.specialFilter){
16567                             this.fireEvent('specialfilter', this);
16568                             this.onLoad();
16569                             return;
16570                         }
16571                         
16572                         this.store.filter(this.displayField, q);
16573                     }
16574                     
16575                     this.store.fireEvent("datachanged", this.store);
16576                     
16577                     this.onLoad();
16578                     
16579                     
16580                 }else{
16581                     
16582                     this.store.baseParams[this.queryParam] = q;
16583                     
16584                     var options = {params : this.getParams(q)};
16585                     
16586                     if(this.loadNext){
16587                         options.add = true;
16588                         options.params.start = this.page * this.pageSize;
16589                     }
16590                     
16591                     this.store.load(options);
16592                     
16593                     /*
16594                      *  this code will make the page width larger, at the beginning, the list not align correctly, 
16595                      *  we should expand the list on onLoad
16596                      *  so command out it
16597                      */
16598 //                    this.expand();
16599                 }
16600             }else{
16601                 this.selectedIndex = -1;
16602                 this.onLoad();   
16603             }
16604         }
16605         
16606         this.loadNext = false;
16607     },
16608     
16609     // private
16610     getParams : function(q){
16611         var p = {};
16612         //p[this.queryParam] = q;
16613         
16614         if(this.pageSize){
16615             p.start = 0;
16616             p.limit = this.pageSize;
16617         }
16618         return p;
16619     },
16620
16621     /**
16622      * Hides the dropdown list if it is currently expanded. Fires the 'collapse' event on completion.
16623      */
16624     collapse : function(){
16625         if(!this.isExpanded()){
16626             return;
16627         }
16628         
16629         this.list.hide();
16630         
16631         this.hasFocus = false;
16632         
16633         if(this.tickable){
16634             this.okBtn.hide();
16635             this.cancelBtn.hide();
16636             this.trigger.show();
16637             
16638             if(this.editable){
16639                 this.tickableInputEl().dom.value = '';
16640                 this.tickableInputEl().blur();
16641             }
16642             
16643         }
16644         
16645         Roo.get(document).un('mousedown', this.collapseIf, this);
16646         Roo.get(document).un('mousewheel', this.collapseIf, this);
16647         if (!this.editable) {
16648             Roo.get(document).un('keydown', this.listKeyPress, this);
16649         }
16650         this.fireEvent('collapse', this);
16651         
16652         this.validate();
16653     },
16654
16655     // private
16656     collapseIf : function(e){
16657         var in_combo  = e.within(this.el);
16658         var in_list =  e.within(this.list);
16659         var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
16660         
16661         if (in_combo || in_list || is_list) {
16662             //e.stopPropagation();
16663             return;
16664         }
16665         
16666         if(this.tickable){
16667             this.onTickableFooterButtonClick(e, false, false);
16668         }
16669
16670         this.collapse();
16671         
16672     },
16673
16674     /**
16675      * Expands the dropdown list if it is currently hidden. Fires the 'expand' event on completion.
16676      */
16677     expand : function(){
16678        
16679         if(this.isExpanded() || !this.hasFocus){
16680             return;
16681         }
16682         
16683         var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
16684         this.list.setWidth(lw);
16685         
16686         Roo.log('expand');
16687         
16688         this.list.show();
16689         
16690         this.restrictHeight();
16691         
16692         if(this.tickable){
16693             
16694             this.tickItems = Roo.apply([], this.item);
16695             
16696             this.okBtn.show();
16697             this.cancelBtn.show();
16698             this.trigger.hide();
16699             
16700             if(this.editable){
16701                 this.tickableInputEl().focus();
16702             }
16703             
16704         }
16705         
16706         Roo.get(document).on('mousedown', this.collapseIf, this);
16707         Roo.get(document).on('mousewheel', this.collapseIf, this);
16708         if (!this.editable) {
16709             Roo.get(document).on('keydown', this.listKeyPress, this);
16710         }
16711         
16712         this.fireEvent('expand', this);
16713     },
16714
16715     // private
16716     // Implements the default empty TriggerField.onTriggerClick function
16717     onTriggerClick : function(e)
16718     {
16719         Roo.log('trigger click');
16720         
16721         if(this.disabled || !this.triggerList){
16722             return;
16723         }
16724         
16725         this.page = 0;
16726         this.loadNext = false;
16727         
16728         if(this.isExpanded()){
16729             this.collapse();
16730             if (!this.blockFocus) {
16731                 this.inputEl().focus();
16732             }
16733             
16734         }else {
16735             this.hasFocus = true;
16736             if(this.triggerAction == 'all') {
16737                 this.doQuery(this.allQuery, true);
16738             } else {
16739                 this.doQuery(this.getRawValue());
16740             }
16741             if (!this.blockFocus) {
16742                 this.inputEl().focus();
16743             }
16744         }
16745     },
16746     
16747     onTickableTriggerClick : function(e)
16748     {
16749         if(this.disabled){
16750             return;
16751         }
16752         
16753         this.page = 0;
16754         this.loadNext = false;
16755         this.hasFocus = true;
16756         
16757         if(this.triggerAction == 'all') {
16758             this.doQuery(this.allQuery, true);
16759         } else {
16760             this.doQuery(this.getRawValue());
16761         }
16762     },
16763     
16764     onSearchFieldClick : function(e)
16765     {
16766         if(this.hasFocus && !this.disabled && e.getTarget().nodeName.toLowerCase() != 'button'){
16767             this.onTickableFooterButtonClick(e, false, false);
16768             return;
16769         }
16770         
16771         if(this.hasFocus || this.disabled || e.getTarget().nodeName.toLowerCase() == 'button'){
16772             return;
16773         }
16774         
16775         this.page = 0;
16776         this.loadNext = false;
16777         this.hasFocus = true;
16778         
16779         if(this.triggerAction == 'all') {
16780             this.doQuery(this.allQuery, true);
16781         } else {
16782             this.doQuery(this.getRawValue());
16783         }
16784     },
16785     
16786     listKeyPress : function(e)
16787     {
16788         //Roo.log('listkeypress');
16789         // scroll to first matching element based on key pres..
16790         if (e.isSpecialKey()) {
16791             return false;
16792         }
16793         var k = String.fromCharCode(e.getKey()).toUpperCase();
16794         //Roo.log(k);
16795         var match  = false;
16796         var csel = this.view.getSelectedNodes();
16797         var cselitem = false;
16798         if (csel.length) {
16799             var ix = this.view.indexOf(csel[0]);
16800             cselitem  = this.store.getAt(ix);
16801             if (!cselitem.get(this.displayField) || cselitem.get(this.displayField).substring(0,1).toUpperCase() != k) {
16802                 cselitem = false;
16803             }
16804             
16805         }
16806         
16807         this.store.each(function(v) { 
16808             if (cselitem) {
16809                 // start at existing selection.
16810                 if (cselitem.id == v.id) {
16811                     cselitem = false;
16812                 }
16813                 return true;
16814             }
16815                 
16816             if (v.get(this.displayField) && v.get(this.displayField).substring(0,1).toUpperCase() == k) {
16817                 match = this.store.indexOf(v);
16818                 return false;
16819             }
16820             return true;
16821         }, this);
16822         
16823         if (match === false) {
16824             return true; // no more action?
16825         }
16826         // scroll to?
16827         this.view.select(match);
16828         var sn = Roo.get(this.view.getSelectedNodes()[0]);
16829         sn.scrollIntoView(sn.dom.parentNode, false);
16830     },
16831     
16832     onViewScroll : function(e, t){
16833         
16834         if(this.view.el.getScroll().top == 0 ||this.view.el.getScroll().top < this.view.el.dom.scrollHeight - this.view.el.dom.clientHeight || !this.hasFocus || !this.append || this.hasQuery){
16835             return;
16836         }
16837         
16838         this.hasQuery = true;
16839         
16840         this.loading = this.list.select('.loading', true).first();
16841         
16842         if(this.loading === null){
16843             this.list.createChild({
16844                 tag: 'div',
16845                 cls: 'loading roo-select2-more-results roo-select2-active',
16846                 html: 'Loading more results...'
16847             });
16848             
16849             this.loading = this.list.select('.loading', true).first();
16850             
16851             this.loading.setVisibilityMode(Roo.Element.DISPLAY);
16852             
16853             this.loading.hide();
16854         }
16855         
16856         this.loading.show();
16857         
16858         var _combo = this;
16859         
16860         this.page++;
16861         this.loadNext = true;
16862         
16863         (function() { _combo.doQuery(_combo.allQuery, true); }).defer(500);
16864         
16865         return;
16866     },
16867     
16868     addItem : function(o)
16869     {   
16870         var dv = ''; // display value
16871         
16872         if (this.displayField) {
16873             dv = !o || typeof(o[this.displayField]) == 'undefined' ? '' : o[this.displayField];
16874         } else {
16875             // this is an error condition!!!
16876             Roo.log('no  displayField value set for '+ (this.name ? this.name : this.id));
16877         }
16878         
16879         if(!dv.length){
16880             return;
16881         }
16882         
16883         var choice = this.choices.createChild({
16884             tag: 'li',
16885             cls: 'roo-select2-search-choice',
16886             cn: [
16887                 {
16888                     tag: 'div',
16889                     html: dv
16890                 },
16891                 {
16892                     tag: 'a',
16893                     href: '#',
16894                     cls: 'roo-select2-search-choice-close fa fa-times',
16895                     tabindex: '-1'
16896                 }
16897             ]
16898             
16899         }, this.searchField);
16900         
16901         var close = choice.select('a.roo-select2-search-choice-close', true).first();
16902         
16903         close.on('click', this.onRemoveItem, this, { item : choice, data : o} );
16904         
16905         this.item.push(o);
16906         
16907         this.lastData = o;
16908         
16909         this.syncValue();
16910         
16911         this.inputEl().dom.value = '';
16912         
16913         this.validate();
16914     },
16915     
16916     onRemoveItem : function(e, _self, o)
16917     {
16918         e.preventDefault();
16919         
16920         this.lastItem = Roo.apply([], this.item);
16921         
16922         var index = this.item.indexOf(o.data) * 1;
16923         
16924         if( index < 0){
16925             Roo.log('not this item?!');
16926             return;
16927         }
16928         
16929         this.item.splice(index, 1);
16930         o.item.remove();
16931         
16932         this.syncValue();
16933         
16934         this.fireEvent('remove', this, e);
16935         
16936         this.validate();
16937         
16938     },
16939     
16940     syncValue : function()
16941     {
16942         if(!this.item.length){
16943             this.clearValue();
16944             return;
16945         }
16946             
16947         var value = [];
16948         var _this = this;
16949         Roo.each(this.item, function(i){
16950             if(_this.valueField){
16951                 value.push(i[_this.valueField]);
16952                 return;
16953             }
16954
16955             value.push(i);
16956         });
16957
16958         this.value = value.join(',');
16959
16960         if(this.hiddenField){
16961             this.hiddenField.dom.value = this.value;
16962         }
16963         
16964         this.store.fireEvent("datachanged", this.store);
16965         
16966         this.validate();
16967     },
16968     
16969     clearItem : function()
16970     {
16971         if(!this.multiple){
16972             return;
16973         }
16974         
16975         this.item = [];
16976         
16977         Roo.each(this.choices.select('>li.roo-select2-search-choice', true).elements, function(c){
16978            c.remove();
16979         });
16980         
16981         this.syncValue();
16982         
16983         this.validate();
16984         
16985         if(this.tickable && !Roo.isTouch){
16986             this.view.refresh();
16987         }
16988     },
16989     
16990     inputEl: function ()
16991     {
16992         if(Roo.isIOS && this.useNativeIOS){
16993             return this.el.select('select.roo-ios-select', true).first();
16994         }
16995         
16996         if(Roo.isTouch && this.mobileTouchView){
16997             return this.el.select('input.form-control',true).first();
16998         }
16999         
17000         if(this.tickable){
17001             return this.searchField;
17002         }
17003         
17004         return this.el.select('input.form-control',true).first();
17005     },
17006     
17007     onTickableFooterButtonClick : function(e, btn, el)
17008     {
17009         e.preventDefault();
17010         
17011         this.lastItem = Roo.apply([], this.item);
17012         
17013         if(btn && btn.name == 'cancel'){
17014             this.tickItems = Roo.apply([], this.item);
17015             this.collapse();
17016             return;
17017         }
17018         
17019         this.clearItem();
17020         
17021         var _this = this;
17022         
17023         Roo.each(this.tickItems, function(o){
17024             _this.addItem(o);
17025         });
17026         
17027         this.collapse();
17028         
17029     },
17030     
17031     validate : function()
17032     {
17033         if(this.getVisibilityEl().hasClass('hidden')){
17034             return true;
17035         }
17036         
17037         var v = this.getRawValue();
17038         
17039         if(this.multiple){
17040             v = this.getValue();
17041         }
17042         
17043         if(this.disabled || this.allowBlank || v.length){
17044             this.markValid();
17045             return true;
17046         }
17047         
17048         this.markInvalid();
17049         return false;
17050     },
17051     
17052     tickableInputEl : function()
17053     {
17054         if(!this.tickable || !this.editable){
17055             return this.inputEl();
17056         }
17057         
17058         return this.inputEl().select('.roo-select2-search-field-input', true).first();
17059     },
17060     
17061     
17062     getAutoCreateTouchView : function()
17063     {
17064         var id = Roo.id();
17065         
17066         var cfg = {
17067             cls: 'form-group' //input-group
17068         };
17069         
17070         var input =  {
17071             tag: 'input',
17072             id : id,
17073             type : this.inputType,
17074             cls : 'form-control x-combo-noedit',
17075             autocomplete: 'new-password',
17076             placeholder : this.placeholder || '',
17077             readonly : true
17078         };
17079         
17080         if (this.name) {
17081             input.name = this.name;
17082         }
17083         
17084         if (this.size) {
17085             input.cls += ' input-' + this.size;
17086         }
17087         
17088         if (this.disabled) {
17089             input.disabled = true;
17090         }
17091         
17092         var inputblock = {
17093             cls : 'roo-combobox-wrap',
17094             cn : [
17095                 input
17096             ]
17097         };
17098         
17099         if(this.before){
17100             inputblock.cls += ' input-group';
17101             
17102             inputblock.cn.unshift({
17103                 tag :'span',
17104                 cls : 'input-group-addon input-group-prepend input-group-text',
17105                 html : this.before
17106             });
17107         }
17108         
17109         if(this.removable && !this.multiple){
17110             inputblock.cls += ' roo-removable';
17111             
17112             inputblock.cn.push({
17113                 tag: 'button',
17114                 html : 'x',
17115                 cls : 'roo-combo-removable-btn close'
17116             });
17117         }
17118
17119         if(this.hasFeedback && !this.allowBlank){
17120             
17121             inputblock.cls += ' has-feedback';
17122             
17123             inputblock.cn.push({
17124                 tag: 'span',
17125                 cls: 'glyphicon form-control-feedback'
17126             });
17127             
17128         }
17129         
17130         if (this.after) {
17131             
17132             inputblock.cls += (this.before) ? '' : ' input-group';
17133             
17134             inputblock.cn.push({
17135                 tag :'span',
17136                 cls : 'input-group-addon input-group-append input-group-text',
17137                 html : this.after
17138             });
17139         }
17140
17141         
17142         var ibwrap = inputblock;
17143         
17144         if(this.multiple){
17145             ibwrap = {
17146                 tag: 'ul',
17147                 cls: 'roo-select2-choices',
17148                 cn:[
17149                     {
17150                         tag: 'li',
17151                         cls: 'roo-select2-search-field',
17152                         cn: [
17153
17154                             inputblock
17155                         ]
17156                     }
17157                 ]
17158             };
17159         
17160             
17161         }
17162         
17163         var combobox = {
17164             cls: 'roo-select2-container input-group roo-touchview-combobox ',
17165             cn: [
17166                 {
17167                     tag: 'input',
17168                     type : 'hidden',
17169                     cls: 'form-hidden-field'
17170                 },
17171                 ibwrap
17172             ]
17173         };
17174         
17175         if(!this.multiple && this.showToggleBtn){
17176             
17177             var caret = {
17178                 cls: 'caret'
17179             };
17180             
17181             if (this.caret != false) {
17182                 caret = {
17183                      tag: 'i',
17184                      cls: 'fa fa-' + this.caret
17185                 };
17186                 
17187             }
17188             
17189             combobox.cn.push({
17190                 tag :'span',
17191                 cls : 'input-group-addon input-group-append input-group-text btn dropdown-toggle',
17192                 cn : [
17193                     Roo.bootstrap.version == 3 ? caret : '',
17194                     {
17195                         tag: 'span',
17196                         cls: 'combobox-clear',
17197                         cn  : [
17198                             {
17199                                 tag : 'i',
17200                                 cls: 'icon-remove'
17201                             }
17202                         ]
17203                     }
17204                 ]
17205
17206             })
17207         }
17208         
17209         if(this.multiple){
17210             combobox.cls += ' roo-select2-container-multi';
17211         }
17212         
17213         var align = this.labelAlign || this.parentLabelAlign();
17214         
17215         if (align ==='left' && this.fieldLabel.length) {
17216
17217             cfg.cn = [
17218                 {
17219                    tag : 'i',
17220                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17221                    tooltip : 'This field is required'
17222                 },
17223                 {
17224                     tag: 'label',
17225                     cls : 'control-label col-form-label',
17226                     html : this.fieldLabel
17227
17228                 },
17229                 {
17230                     cls : 'roo-combobox-wrap ', 
17231                     cn: [
17232                         combobox
17233                     ]
17234                 }
17235             ];
17236             
17237             var labelCfg = cfg.cn[1];
17238             var contentCfg = cfg.cn[2];
17239             
17240
17241             if(this.indicatorpos == 'right'){
17242                 cfg.cn = [
17243                     {
17244                         tag: 'label',
17245                         'for' :  id,
17246                         cls : 'control-label col-form-label',
17247                         cn : [
17248                             {
17249                                 tag : 'span',
17250                                 html : this.fieldLabel
17251                             },
17252                             {
17253                                 tag : 'i',
17254                                 cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17255                                 tooltip : 'This field is required'
17256                             }
17257                         ]
17258                     },
17259                     {
17260                         cls : "roo-combobox-wrap ",
17261                         cn: [
17262                             combobox
17263                         ]
17264                     }
17265
17266                 ];
17267                 
17268                 labelCfg = cfg.cn[0];
17269                 contentCfg = cfg.cn[1];
17270             }
17271             
17272            
17273             
17274             if(this.labelWidth > 12){
17275                 labelCfg.style = "width: " + this.labelWidth + 'px';
17276             }
17277            
17278             if(this.labelWidth < 13 && this.labelmd == 0){
17279                 this.labelmd = this.labelWidth;
17280             }
17281             
17282             if(this.labellg > 0){
17283                 labelCfg.cls += ' col-lg-' + this.labellg;
17284                 contentCfg.cls += ' col-lg-' + (12 - this.labellg);
17285             }
17286             
17287             if(this.labelmd > 0){
17288                 labelCfg.cls += ' col-md-' + this.labelmd;
17289                 contentCfg.cls += ' col-md-' + (12 - this.labelmd);
17290             }
17291             
17292             if(this.labelsm > 0){
17293                 labelCfg.cls += ' col-sm-' + this.labelsm;
17294                 contentCfg.cls += ' col-sm-' + (12 - this.labelsm);
17295             }
17296             
17297             if(this.labelxs > 0){
17298                 labelCfg.cls += ' col-xs-' + this.labelxs;
17299                 contentCfg.cls += ' col-xs-' + (12 - this.labelxs);
17300             }
17301                 
17302                 
17303         } else if ( this.fieldLabel.length) {
17304             cfg.cn = [
17305                 {
17306                    tag : 'i',
17307                    cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
17308                    tooltip : 'This field is required'
17309                 },
17310                 {
17311                     tag: 'label',
17312                     cls : 'control-label',
17313                     html : this.fieldLabel
17314
17315                 },
17316                 {
17317                     cls : '', 
17318                     cn: [
17319                         combobox
17320                     ]
17321                 }
17322             ];
17323             
17324             if(this.indicatorpos == 'right'){
17325                 cfg.cn = [
17326                     {
17327                         tag: 'label',
17328                         cls : 'control-label',
17329                         html : this.fieldLabel,
17330                         cn : [
17331                             {
17332                                tag : 'i',
17333                                cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
17334                                tooltip : 'This field is required'
17335                             }
17336                         ]
17337                     },
17338                     {
17339                         cls : '', 
17340                         cn: [
17341                             combobox
17342                         ]
17343                     }
17344                 ];
17345             }
17346         } else {
17347             cfg.cn = combobox;    
17348         }
17349         
17350         
17351         var settings = this;
17352         
17353         ['xs','sm','md','lg'].map(function(size){
17354             if (settings[size]) {
17355                 cfg.cls += ' col-' + size + '-' + settings[size];
17356             }
17357         });
17358         
17359         return cfg;
17360     },
17361     
17362     initTouchView : function()
17363     {
17364         this.renderTouchView();
17365         
17366         this.touchViewEl.on('scroll', function(){
17367             this.el.dom.scrollTop = 0;
17368         }, this);
17369         
17370         this.originalValue = this.getValue();
17371         
17372         this.triggerEl = this.el.select('span.dropdown-toggle',true).first();
17373         
17374         this.inputEl().on("click", this.showTouchView, this);
17375         if (this.triggerEl) {
17376             this.triggerEl.on("click", this.showTouchView, this);
17377         }
17378         
17379         
17380         this.touchViewFooterEl.select('.roo-touch-view-cancel', true).first().on('click', this.hideTouchView, this);
17381         this.touchViewFooterEl.select('.roo-touch-view-ok', true).first().on('click', this.setTouchViewValue, this);
17382         
17383         this.maskEl = new Roo.LoadMask(this.touchViewEl, { store : this.store, msgCls: 'roo-el-mask-msg' });
17384         
17385         this.store.on('beforeload', this.onTouchViewBeforeLoad, this);
17386         this.store.on('load', this.onTouchViewLoad, this);
17387         this.store.on('loadexception', this.onTouchViewLoadException, this);
17388         
17389         if(this.hiddenName){
17390             
17391             this.hiddenField = this.el.select('input.form-hidden-field',true).first();
17392             
17393             this.hiddenField.dom.value =
17394                 this.hiddenValue !== undefined ? this.hiddenValue :
17395                 this.value !== undefined ? this.value : '';
17396         
17397             this.el.dom.removeAttribute('name');
17398             this.hiddenField.dom.setAttribute('name', this.hiddenName);
17399         }
17400         
17401         if(this.multiple){
17402             this.choices = this.el.select('ul.roo-select2-choices', true).first();
17403             this.searchField = this.el.select('ul li.roo-select2-search-field', true).first();
17404         }
17405         
17406         if(this.removable && !this.multiple){
17407             var close = this.closeTriggerEl();
17408             if(close){
17409                 close.setVisibilityMode(Roo.Element.DISPLAY).hide();
17410                 close.on('click', this.removeBtnClick, this, close);
17411             }
17412         }
17413         /*
17414          * fix the bug in Safari iOS8
17415          */
17416         this.inputEl().on("focus", function(e){
17417             document.activeElement.blur();
17418         }, this);
17419         
17420         this._touchViewMask = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
17421         
17422         return;
17423         
17424         
17425     },
17426     
17427     renderTouchView : function()
17428     {
17429         this.touchViewEl = Roo.get(document.body).createChild(Roo.bootstrap.ComboBox.touchViewTemplate);
17430         this.touchViewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17431         
17432         this.touchViewHeaderEl = this.touchViewEl.select('.modal-header', true).first();
17433         this.touchViewHeaderEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17434         
17435         this.touchViewBodyEl = this.touchViewEl.select('.modal-body', true).first();
17436         this.touchViewBodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17437         this.touchViewBodyEl.setStyle('overflow', 'auto');
17438         
17439         this.touchViewListGroup = this.touchViewBodyEl.select('.list-group', true).first();
17440         this.touchViewListGroup.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17441         
17442         this.touchViewFooterEl = this.touchViewEl.select('.modal-footer', true).first();
17443         this.touchViewFooterEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
17444         
17445     },
17446     
17447     showTouchView : function()
17448     {
17449         if(this.disabled){
17450             return;
17451         }
17452         
17453         this.touchViewHeaderEl.hide();
17454
17455         if(this.modalTitle.length){
17456             this.touchViewHeaderEl.dom.innerHTML = this.modalTitle;
17457             this.touchViewHeaderEl.show();
17458         }
17459
17460         this.touchViewEl.setStyle('z-index', Roo.bootstrap.Modal.zIndex++);
17461         this.touchViewEl.show();
17462
17463         this.touchViewEl.select('.modal-dialog', true).first().setStyle({ margin : '0px', width : '100%'});
17464         
17465         //this.touchViewEl.select('.modal-dialog > .modal-content', true).first().setSize(
17466         //        Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
17467
17468         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17469
17470         if(this.modalTitle.length){
17471             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17472         }
17473         
17474         this.touchViewBodyEl.setHeight(bodyHeight);
17475
17476         if(this.animate){
17477             var _this = this;
17478             (function(){ _this.touchViewEl.addClass(['in','show']); }).defer(50);
17479         }else{
17480             this.touchViewEl.addClass(['in','show']);
17481         }
17482         
17483         if(this._touchViewMask){
17484             Roo.get(document.body).addClass("x-body-masked");
17485             this._touchViewMask.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
17486             this._touchViewMask.setStyle('z-index', 10000);
17487             this._touchViewMask.addClass('show');
17488         }
17489         
17490         this.doTouchViewQuery();
17491         
17492     },
17493     
17494     hideTouchView : function()
17495     {
17496         this.touchViewEl.removeClass(['in','show']);
17497
17498         if(this.animate){
17499             var _this = this;
17500             (function(){ _this.touchViewEl.setStyle('display', 'none'); }).defer(150);
17501         }else{
17502             this.touchViewEl.setStyle('display', 'none');
17503         }
17504         
17505         if(this._touchViewMask){
17506             this._touchViewMask.removeClass('show');
17507             Roo.get(document.body).removeClass("x-body-masked");
17508         }
17509     },
17510     
17511     setTouchViewValue : function()
17512     {
17513         if(this.multiple){
17514             this.clearItem();
17515         
17516             var _this = this;
17517
17518             Roo.each(this.tickItems, function(o){
17519                 this.addItem(o);
17520             }, this);
17521         }
17522         
17523         this.hideTouchView();
17524     },
17525     
17526     doTouchViewQuery : function()
17527     {
17528         var qe = {
17529             query: '',
17530             forceAll: true,
17531             combo: this,
17532             cancel:false
17533         };
17534         
17535         if(this.fireEvent('beforequery', qe) ===false || qe.cancel){
17536             return false;
17537         }
17538         
17539         if(!this.alwaysQuery || this.mode == 'local'){
17540             this.onTouchViewLoad();
17541             return;
17542         }
17543         
17544         this.store.load();
17545     },
17546     
17547     onTouchViewBeforeLoad : function(combo,opts)
17548     {
17549         return;
17550     },
17551
17552     // private
17553     onTouchViewLoad : function()
17554     {
17555         if(this.store.getCount() < 1){
17556             this.onTouchViewEmptyResults();
17557             return;
17558         }
17559         
17560         this.clearTouchView();
17561         
17562         var rawValue = this.getRawValue();
17563         
17564         var template = (this.multiple) ? Roo.bootstrap.ComboBox.listItemCheckbox : Roo.bootstrap.ComboBox.listItemRadio;
17565         
17566         this.tickItems = [];
17567         
17568         this.store.data.each(function(d, rowIndex){
17569             var row = this.touchViewListGroup.createChild(template);
17570             
17571             if(typeof(d.data.cls) != 'undefined' && d.data.cls.length){
17572                 row.addClass(d.data.cls);
17573             }
17574             
17575             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17576                 var cfg = {
17577                     data : d.data,
17578                     html : d.data[this.displayField]
17579                 };
17580                 
17581                 if(this.fireEvent('touchviewdisplay', this, cfg) !== false){
17582                     row.select('.roo-combobox-list-group-item-value', true).first().dom.innerHTML = cfg.html;
17583                 }
17584             }
17585             row.removeClass('selected');
17586             if(!this.multiple && this.valueField &&
17587                     typeof(d.data[this.valueField]) != 'undefined' && d.data[this.valueField] == this.getValue())
17588             {
17589                 // radio buttons..
17590                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17591                 row.addClass('selected');
17592             }
17593             
17594             if(this.multiple && this.valueField &&
17595                     typeof(d.data[this.valueField]) != 'undefined' && this.getValue().indexOf(d.data[this.valueField]) != -1)
17596             {
17597                 
17598                 // checkboxes...
17599                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17600                 this.tickItems.push(d.data);
17601             }
17602             
17603             row.on('click', this.onTouchViewClick, this, {row : row, rowIndex : rowIndex});
17604             
17605         }, this);
17606         
17607         var firstChecked = this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).first();
17608         
17609         var bodyHeight = Roo.lib.Dom.getViewHeight() - this.touchViewFooterEl.getHeight() + this.touchViewBodyEl.getPadding('tb');
17610
17611         if(this.modalTitle.length){
17612             bodyHeight = bodyHeight - this.touchViewHeaderEl.getHeight();
17613         }
17614
17615         var listHeight = this.touchViewListGroup.getHeight() + this.touchViewBodyEl.getPadding('tb') * 2;
17616         
17617         if(this.mobile_restrict_height && listHeight < bodyHeight){
17618             this.touchViewBodyEl.setHeight(listHeight);
17619         }
17620         
17621         var _this = this;
17622         
17623         if(firstChecked && listHeight > bodyHeight){
17624             (function() { firstChecked.findParent('li').scrollIntoView(_this.touchViewListGroup.dom); }).defer(500);
17625         }
17626         
17627     },
17628     
17629     onTouchViewLoadException : function()
17630     {
17631         this.hideTouchView();
17632     },
17633     
17634     onTouchViewEmptyResults : function()
17635     {
17636         this.clearTouchView();
17637         
17638         this.touchViewListGroup.createChild(Roo.bootstrap.ComboBox.emptyResult);
17639         
17640         this.touchViewListGroup.select('.roo-combobox-touch-view-empty-result', true).first().dom.innerHTML = this.emptyResultText;
17641         
17642     },
17643     
17644     clearTouchView : function()
17645     {
17646         this.touchViewListGroup.dom.innerHTML = '';
17647     },
17648     
17649     onTouchViewClick : function(e, el, o)
17650     {
17651         e.preventDefault();
17652         
17653         var row = o.row;
17654         var rowIndex = o.rowIndex;
17655         
17656         var r = this.store.getAt(rowIndex);
17657         
17658         if(this.fireEvent('beforeselect', this, r, rowIndex) !== false){
17659             
17660             if(!this.multiple){
17661                 Roo.each(this.touchViewListGroup.select('.list-group-item > .roo-combobox-list-group-item-box > input:checked', true).elements, function(c){
17662                     c.dom.removeAttribute('checked');
17663                 }, this);
17664
17665                 row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17666
17667                 this.setFromData(r.data);
17668
17669                 var close = this.closeTriggerEl();
17670
17671                 if(close){
17672                     close.show();
17673                 }
17674
17675                 this.hideTouchView();
17676
17677                 this.fireEvent('select', this, r, rowIndex);
17678
17679                 return;
17680             }
17681
17682             if(this.valueField && typeof(r.data[this.valueField]) != 'undefined' && this.getValue().indexOf(r.data[this.valueField]) != -1){
17683                 row.select('.roo-combobox-list-group-item-box > input', true).first().dom.removeAttribute('checked');
17684                 this.tickItems.splice(this.tickItems.indexOf(r.data), 1);
17685                 return;
17686             }
17687
17688             row.select('.roo-combobox-list-group-item-box > input', true).first().attr('checked', true);
17689             this.addItem(r.data);
17690             this.tickItems.push(r.data);
17691         }
17692     },
17693     
17694     getAutoCreateNativeIOS : function()
17695     {
17696         var cfg = {
17697             cls: 'form-group' //input-group,
17698         };
17699         
17700         var combobox =  {
17701             tag: 'select',
17702             cls : 'roo-ios-select'
17703         };
17704         
17705         if (this.name) {
17706             combobox.name = this.name;
17707         }
17708         
17709         if (this.disabled) {
17710             combobox.disabled = true;
17711         }
17712         
17713         var settings = this;
17714         
17715         ['xs','sm','md','lg'].map(function(size){
17716             if (settings[size]) {
17717                 cfg.cls += ' col-' + size + '-' + settings[size];
17718             }
17719         });
17720         
17721         cfg.cn = combobox;
17722         
17723         return cfg;
17724         
17725     },
17726     
17727     initIOSView : function()
17728     {
17729         this.store.on('load', this.onIOSViewLoad, this);
17730         
17731         return;
17732     },
17733     
17734     onIOSViewLoad : function()
17735     {
17736         if(this.store.getCount() < 1){
17737             return;
17738         }
17739         
17740         this.clearIOSView();
17741         
17742         if(this.allowBlank) {
17743             
17744             var default_text = '-- SELECT --';
17745             
17746             if(this.placeholder.length){
17747                 default_text = this.placeholder;
17748             }
17749             
17750             if(this.emptyTitle.length){
17751                 default_text += ' - ' + this.emptyTitle + ' -';
17752             }
17753             
17754             var opt = this.inputEl().createChild({
17755                 tag: 'option',
17756                 value : 0,
17757                 html : default_text
17758             });
17759             
17760             var o = {};
17761             o[this.valueField] = 0;
17762             o[this.displayField] = default_text;
17763             
17764             this.ios_options.push({
17765                 data : o,
17766                 el : opt
17767             });
17768             
17769         }
17770         
17771         this.store.data.each(function(d, rowIndex){
17772             
17773             var html = '';
17774             
17775             if(this.displayField && typeof(d.data[this.displayField]) != 'undefined'){
17776                 html = d.data[this.displayField];
17777             }
17778             
17779             var value = '';
17780             
17781             if(this.valueField && typeof(d.data[this.valueField]) != 'undefined'){
17782                 value = d.data[this.valueField];
17783             }
17784             
17785             var option = {
17786                 tag: 'option',
17787                 value : value,
17788                 html : html
17789             };
17790             
17791             if(this.value == d.data[this.valueField]){
17792                 option['selected'] = true;
17793             }
17794             
17795             var opt = this.inputEl().createChild(option);
17796             
17797             this.ios_options.push({
17798                 data : d.data,
17799                 el : opt
17800             });
17801             
17802         }, this);
17803         
17804         this.inputEl().on('change', function(){
17805            this.fireEvent('select', this);
17806         }, this);
17807         
17808     },
17809     
17810     clearIOSView: function()
17811     {
17812         this.inputEl().dom.innerHTML = '';
17813         
17814         this.ios_options = [];
17815     },
17816     
17817     setIOSValue: function(v)
17818     {
17819         this.value = v;
17820         
17821         if(!this.ios_options){
17822             return;
17823         }
17824         
17825         Roo.each(this.ios_options, function(opts){
17826            
17827            opts.el.dom.removeAttribute('selected');
17828            
17829            if(opts.data[this.valueField] != v){
17830                return;
17831            }
17832            
17833            opts.el.dom.setAttribute('selected', true);
17834            
17835         }, this);
17836     }
17837
17838     /** 
17839     * @cfg {Boolean} grow 
17840     * @hide 
17841     */
17842     /** 
17843     * @cfg {Number} growMin 
17844     * @hide 
17845     */
17846     /** 
17847     * @cfg {Number} growMax 
17848     * @hide 
17849     */
17850     /**
17851      * @hide
17852      * @method autoSize
17853      */
17854 });
17855
17856 Roo.apply(Roo.bootstrap.ComboBox,  {
17857     
17858     header : {
17859         tag: 'div',
17860         cls: 'modal-header',
17861         cn: [
17862             {
17863                 tag: 'h4',
17864                 cls: 'modal-title'
17865             }
17866         ]
17867     },
17868     
17869     body : {
17870         tag: 'div',
17871         cls: 'modal-body',
17872         cn: [
17873             {
17874                 tag: 'ul',
17875                 cls: 'list-group'
17876             }
17877         ]
17878     },
17879     
17880     listItemRadio : {
17881         tag: 'li',
17882         cls: 'list-group-item',
17883         cn: [
17884             {
17885                 tag: 'span',
17886                 cls: 'roo-combobox-list-group-item-value'
17887             },
17888             {
17889                 tag: 'div',
17890                 cls: 'roo-combobox-list-group-item-box pull-xs-right radio-inline radio radio-info',
17891                 cn: [
17892                     {
17893                         tag: 'input',
17894                         type: 'radio'
17895                     },
17896                     {
17897                         tag: 'label'
17898                     }
17899                 ]
17900             }
17901         ]
17902     },
17903     
17904     listItemCheckbox : {
17905         tag: 'li',
17906         cls: 'list-group-item',
17907         cn: [
17908             {
17909                 tag: 'span',
17910                 cls: 'roo-combobox-list-group-item-value'
17911             },
17912             {
17913                 tag: 'div',
17914                 cls: 'roo-combobox-list-group-item-box pull-xs-right checkbox-inline checkbox checkbox-info',
17915                 cn: [
17916                     {
17917                         tag: 'input',
17918                         type: 'checkbox'
17919                     },
17920                     {
17921                         tag: 'label'
17922                     }
17923                 ]
17924             }
17925         ]
17926     },
17927     
17928     emptyResult : {
17929         tag: 'div',
17930         cls: 'alert alert-danger roo-combobox-touch-view-empty-result'
17931     },
17932     
17933     footer : {
17934         tag: 'div',
17935         cls: 'modal-footer',
17936         cn: [
17937             {
17938                 tag: 'div',
17939                 cls: 'row',
17940                 cn: [
17941                     {
17942                         tag: 'div',
17943                         cls: 'col-xs-6 text-left',
17944                         cn: {
17945                             tag: 'button',
17946                             cls: 'btn btn-danger roo-touch-view-cancel',
17947                             html: 'Cancel'
17948                         }
17949                     },
17950                     {
17951                         tag: 'div',
17952                         cls: 'col-xs-6 text-right',
17953                         cn: {
17954                             tag: 'button',
17955                             cls: 'btn btn-success roo-touch-view-ok',
17956                             html: 'OK'
17957                         }
17958                     }
17959                 ]
17960             }
17961         ]
17962         
17963     }
17964 });
17965
17966 Roo.apply(Roo.bootstrap.ComboBox,  {
17967     
17968     touchViewTemplate : {
17969         tag: 'div',
17970         cls: 'modal fade roo-combobox-touch-view',
17971         cn: [
17972             {
17973                 tag: 'div',
17974                 cls: 'modal-dialog',
17975                 style : 'position:fixed', // we have to fix position....
17976                 cn: [
17977                     {
17978                         tag: 'div',
17979                         cls: 'modal-content',
17980                         cn: [
17981                             Roo.bootstrap.ComboBox.header,
17982                             Roo.bootstrap.ComboBox.body,
17983                             Roo.bootstrap.ComboBox.footer
17984                         ]
17985                     }
17986                 ]
17987             }
17988         ]
17989     }
17990 });/*
17991  * Based on:
17992  * Ext JS Library 1.1.1
17993  * Copyright(c) 2006-2007, Ext JS, LLC.
17994  *
17995  * Originally Released Under LGPL - original licence link has changed is not relivant.
17996  *
17997  * Fork - LGPL
17998  * <script type="text/javascript">
17999  */
18000
18001 /**
18002  * @class Roo.View
18003  * @extends Roo.util.Observable
18004  * Create a "View" for an element based on a data model or UpdateManager and the supplied DomHelper template. 
18005  * This class also supports single and multi selection modes. <br>
18006  * Create a data model bound view:
18007  <pre><code>
18008  var store = new Roo.data.Store(...);
18009
18010  var view = new Roo.View({
18011     el : "my-element",
18012     tpl : '&lt;div id="{0}"&gt;{2} - {1}&lt;/div&gt;', // auto create template
18013  
18014     singleSelect: true,
18015     selectedClass: "ydataview-selected",
18016     store: store
18017  });
18018
18019  // listen for node click?
18020  view.on("click", function(vw, index, node, e){
18021  alert('Node "' + node.id + '" at index: ' + index + " was clicked.");
18022  });
18023
18024  // load XML data
18025  dataModel.load("foobar.xml");
18026  </code></pre>
18027  For an example of creating a JSON/UpdateManager view, see {@link Roo.JsonView}.
18028  * <br><br>
18029  * <b>Note: The root of your template must be a single node. Table/row implementations may work but are not supported due to
18030  * IE"s limited insertion support with tables and Opera"s faulty event bubbling.</b>
18031  * 
18032  * Note: old style constructor is still suported (container, template, config)
18033  * 
18034  * @constructor
18035  * Create a new View
18036  * @param {Object} config The config object
18037  * 
18038  */
18039 Roo.View = function(config, depreciated_tpl, depreciated_config){
18040     
18041     this.parent = false;
18042     
18043     if (typeof(depreciated_tpl) == 'undefined') {
18044         // new way.. - universal constructor.
18045         Roo.apply(this, config);
18046         this.el  = Roo.get(this.el);
18047     } else {
18048         // old format..
18049         this.el  = Roo.get(config);
18050         this.tpl = depreciated_tpl;
18051         Roo.apply(this, depreciated_config);
18052     }
18053     this.wrapEl  = this.el.wrap().wrap();
18054     ///this.el = this.wrapEla.appendChild(document.createElement("div"));
18055     
18056     
18057     if(typeof(this.tpl) == "string"){
18058         this.tpl = new Roo.Template(this.tpl);
18059     } else {
18060         // support xtype ctors..
18061         this.tpl = new Roo.factory(this.tpl, Roo);
18062     }
18063     
18064     
18065     this.tpl.compile();
18066     
18067     /** @private */
18068     this.addEvents({
18069         /**
18070          * @event beforeclick
18071          * Fires before a click is processed. Returns false to cancel the default action.
18072          * @param {Roo.View} this
18073          * @param {Number} index The index of the target node
18074          * @param {HTMLElement} node The target node
18075          * @param {Roo.EventObject} e The raw event object
18076          */
18077             "beforeclick" : true,
18078         /**
18079          * @event click
18080          * Fires when a template node is clicked.
18081          * @param {Roo.View} this
18082          * @param {Number} index The index of the target node
18083          * @param {HTMLElement} node The target node
18084          * @param {Roo.EventObject} e The raw event object
18085          */
18086             "click" : true,
18087         /**
18088          * @event dblclick
18089          * Fires when a template node is double clicked.
18090          * @param {Roo.View} this
18091          * @param {Number} index The index of the target node
18092          * @param {HTMLElement} node The target node
18093          * @param {Roo.EventObject} e The raw event object
18094          */
18095             "dblclick" : true,
18096         /**
18097          * @event contextmenu
18098          * Fires when a template node is right clicked.
18099          * @param {Roo.View} this
18100          * @param {Number} index The index of the target node
18101          * @param {HTMLElement} node The target node
18102          * @param {Roo.EventObject} e The raw event object
18103          */
18104             "contextmenu" : true,
18105         /**
18106          * @event selectionchange
18107          * Fires when the selected nodes change.
18108          * @param {Roo.View} this
18109          * @param {Array} selections Array of the selected nodes
18110          */
18111             "selectionchange" : true,
18112     
18113         /**
18114          * @event beforeselect
18115          * Fires before a selection is made. If any handlers return false, the selection is cancelled.
18116          * @param {Roo.View} this
18117          * @param {HTMLElement} node The node to be selected
18118          * @param {Array} selections Array of currently selected nodes
18119          */
18120             "beforeselect" : true,
18121         /**
18122          * @event preparedata
18123          * Fires on every row to render, to allow you to change the data.
18124          * @param {Roo.View} this
18125          * @param {Object} data to be rendered (change this)
18126          */
18127           "preparedata" : true
18128           
18129           
18130         });
18131
18132
18133
18134     this.el.on({
18135         "click": this.onClick,
18136         "dblclick": this.onDblClick,
18137         "contextmenu": this.onContextMenu,
18138         scope:this
18139     });
18140
18141     this.selections = [];
18142     this.nodes = [];
18143     this.cmp = new Roo.CompositeElementLite([]);
18144     if(this.store){
18145         this.store = Roo.factory(this.store, Roo.data);
18146         this.setStore(this.store, true);
18147     }
18148     
18149     if ( this.footer && this.footer.xtype) {
18150            
18151          var fctr = this.wrapEl.appendChild(document.createElement("div"));
18152         
18153         this.footer.dataSource = this.store;
18154         this.footer.container = fctr;
18155         this.footer = Roo.factory(this.footer, Roo);
18156         fctr.insertFirst(this.el);
18157         
18158         // this is a bit insane - as the paging toolbar seems to detach the el..
18159 //        dom.parentNode.parentNode.parentNode
18160          // they get detached?
18161     }
18162     
18163     
18164     Roo.View.superclass.constructor.call(this);
18165     
18166     
18167 };
18168
18169 Roo.extend(Roo.View, Roo.util.Observable, {
18170     
18171      /**
18172      * @cfg {Roo.data.Store} store Data store to load data from.
18173      */
18174     store : false,
18175     
18176     /**
18177      * @cfg {String|Roo.Element} el The container element.
18178      */
18179     el : '',
18180     
18181     /**
18182      * @cfg {String|Roo.Template} tpl The template used by this View 
18183      */
18184     tpl : false,
18185     /**
18186      * @cfg {String} dataName the named area of the template to use as the data area
18187      *                          Works with domtemplates roo-name="name"
18188      */
18189     dataName: false,
18190     /**
18191      * @cfg {String} selectedClass The css class to add to selected nodes
18192      */
18193     selectedClass : "x-view-selected",
18194      /**
18195      * @cfg {String} emptyText The empty text to show when nothing is loaded.
18196      */
18197     emptyText : "",
18198     
18199     /**
18200      * @cfg {String} text to display on mask (default Loading)
18201      */
18202     mask : false,
18203     /**
18204      * @cfg {Boolean} multiSelect Allow multiple selection
18205      */
18206     multiSelect : false,
18207     /**
18208      * @cfg {Boolean} singleSelect Allow single selection
18209      */
18210     singleSelect:  false,
18211     
18212     /**
18213      * @cfg {Boolean} toggleSelect - selecting 
18214      */
18215     toggleSelect : false,
18216     
18217     /**
18218      * @cfg {Boolean} tickable - selecting 
18219      */
18220     tickable : false,
18221     
18222     /**
18223      * Returns the element this view is bound to.
18224      * @return {Roo.Element}
18225      */
18226     getEl : function(){
18227         return this.wrapEl;
18228     },
18229     
18230     
18231
18232     /**
18233      * Refreshes the view. - called by datachanged on the store. - do not call directly.
18234      */
18235     refresh : function(){
18236         //Roo.log('refresh');
18237         var t = this.tpl;
18238         
18239         // if we are using something like 'domtemplate', then
18240         // the what gets used is:
18241         // t.applySubtemplate(NAME, data, wrapping data..)
18242         // the outer template then get' applied with
18243         //     the store 'extra data'
18244         // and the body get's added to the
18245         //      roo-name="data" node?
18246         //      <span class='roo-tpl-{name}'></span> ?????
18247         
18248         
18249         
18250         this.clearSelections();
18251         this.el.update("");
18252         var html = [];
18253         var records = this.store.getRange();
18254         if(records.length < 1) {
18255             
18256             // is this valid??  = should it render a template??
18257             
18258             this.el.update(this.emptyText);
18259             return;
18260         }
18261         var el = this.el;
18262         if (this.dataName) {
18263             this.el.update(t.apply(this.store.meta)); //????
18264             el = this.el.child('.roo-tpl-' + this.dataName);
18265         }
18266         
18267         for(var i = 0, len = records.length; i < len; i++){
18268             var data = this.prepareData(records[i].data, i, records[i]);
18269             this.fireEvent("preparedata", this, data, i, records[i]);
18270             
18271             var d = Roo.apply({}, data);
18272             
18273             if(this.tickable){
18274                 Roo.apply(d, {'roo-id' : Roo.id()});
18275                 
18276                 var _this = this;
18277             
18278                 Roo.each(this.parent.item, function(item){
18279                     if(item[_this.parent.valueField] != data[_this.parent.valueField]){
18280                         return;
18281                     }
18282                     Roo.apply(d, {'roo-data-checked' : 'checked'});
18283                 });
18284             }
18285             
18286             html[html.length] = Roo.util.Format.trim(
18287                 this.dataName ?
18288                     t.applySubtemplate(this.dataName, d, this.store.meta) :
18289                     t.apply(d)
18290             );
18291         }
18292         
18293         
18294         
18295         el.update(html.join(""));
18296         this.nodes = el.dom.childNodes;
18297         this.updateIndexes(0);
18298     },
18299     
18300
18301     /**
18302      * Function to override to reformat the data that is sent to
18303      * the template for each node.
18304      * DEPRICATED - use the preparedata event handler.
18305      * @param {Array/Object} data The raw data (array of colData for a data model bound view or
18306      * a JSON object for an UpdateManager bound view).
18307      */
18308     prepareData : function(data, index, record)
18309     {
18310         this.fireEvent("preparedata", this, data, index, record);
18311         return data;
18312     },
18313
18314     onUpdate : function(ds, record){
18315         // Roo.log('on update');   
18316         this.clearSelections();
18317         var index = this.store.indexOf(record);
18318         var n = this.nodes[index];
18319         this.tpl.insertBefore(n, this.prepareData(record.data, index, record));
18320         n.parentNode.removeChild(n);
18321         this.updateIndexes(index, index);
18322     },
18323
18324     
18325     
18326 // --------- FIXME     
18327     onAdd : function(ds, records, index)
18328     {
18329         //Roo.log(['on Add', ds, records, index] );        
18330         this.clearSelections();
18331         if(this.nodes.length == 0){
18332             this.refresh();
18333             return;
18334         }
18335         var n = this.nodes[index];
18336         for(var i = 0, len = records.length; i < len; i++){
18337             var d = this.prepareData(records[i].data, i, records[i]);
18338             if(n){
18339                 this.tpl.insertBefore(n, d);
18340             }else{
18341                 
18342                 this.tpl.append(this.el, d);
18343             }
18344         }
18345         this.updateIndexes(index);
18346     },
18347
18348     onRemove : function(ds, record, index){
18349        // Roo.log('onRemove');
18350         this.clearSelections();
18351         var el = this.dataName  ?
18352             this.el.child('.roo-tpl-' + this.dataName) :
18353             this.el; 
18354         
18355         el.dom.removeChild(this.nodes[index]);
18356         this.updateIndexes(index);
18357     },
18358
18359     /**
18360      * Refresh an individual node.
18361      * @param {Number} index
18362      */
18363     refreshNode : function(index){
18364         this.onUpdate(this.store, this.store.getAt(index));
18365     },
18366
18367     updateIndexes : function(startIndex, endIndex){
18368         var ns = this.nodes;
18369         startIndex = startIndex || 0;
18370         endIndex = endIndex || ns.length - 1;
18371         for(var i = startIndex; i <= endIndex; i++){
18372             ns[i].nodeIndex = i;
18373         }
18374     },
18375
18376     /**
18377      * Changes the data store this view uses and refresh the view.
18378      * @param {Store} store
18379      */
18380     setStore : function(store, initial){
18381         if(!initial && this.store){
18382             this.store.un("datachanged", this.refresh);
18383             this.store.un("add", this.onAdd);
18384             this.store.un("remove", this.onRemove);
18385             this.store.un("update", this.onUpdate);
18386             this.store.un("clear", this.refresh);
18387             this.store.un("beforeload", this.onBeforeLoad);
18388             this.store.un("load", this.onLoad);
18389             this.store.un("loadexception", this.onLoad);
18390         }
18391         if(store){
18392           
18393             store.on("datachanged", this.refresh, this);
18394             store.on("add", this.onAdd, this);
18395             store.on("remove", this.onRemove, this);
18396             store.on("update", this.onUpdate, this);
18397             store.on("clear", this.refresh, this);
18398             store.on("beforeload", this.onBeforeLoad, this);
18399             store.on("load", this.onLoad, this);
18400             store.on("loadexception", this.onLoad, this);
18401         }
18402         
18403         if(store){
18404             this.refresh();
18405         }
18406     },
18407     /**
18408      * onbeforeLoad - masks the loading area.
18409      *
18410      */
18411     onBeforeLoad : function(store,opts)
18412     {
18413          //Roo.log('onBeforeLoad');   
18414         if (!opts.add) {
18415             this.el.update("");
18416         }
18417         this.el.mask(this.mask ? this.mask : "Loading" ); 
18418     },
18419     onLoad : function ()
18420     {
18421         this.el.unmask();
18422     },
18423     
18424
18425     /**
18426      * Returns the template node the passed child belongs to or null if it doesn't belong to one.
18427      * @param {HTMLElement} node
18428      * @return {HTMLElement} The template node
18429      */
18430     findItemFromChild : function(node){
18431         var el = this.dataName  ?
18432             this.el.child('.roo-tpl-' + this.dataName,true) :
18433             this.el.dom; 
18434         
18435         if(!node || node.parentNode == el){
18436                     return node;
18437             }
18438             var p = node.parentNode;
18439             while(p && p != el){
18440             if(p.parentNode == el){
18441                 return p;
18442             }
18443             p = p.parentNode;
18444         }
18445             return null;
18446     },
18447
18448     /** @ignore */
18449     onClick : function(e){
18450         var item = this.findItemFromChild(e.getTarget());
18451         if(item){
18452             var index = this.indexOf(item);
18453             if(this.onItemClick(item, index, e) !== false){
18454                 this.fireEvent("click", this, index, item, e);
18455             }
18456         }else{
18457             this.clearSelections();
18458         }
18459     },
18460
18461     /** @ignore */
18462     onContextMenu : function(e){
18463         var item = this.findItemFromChild(e.getTarget());
18464         if(item){
18465             this.fireEvent("contextmenu", this, this.indexOf(item), item, e);
18466         }
18467     },
18468
18469     /** @ignore */
18470     onDblClick : function(e){
18471         var item = this.findItemFromChild(e.getTarget());
18472         if(item){
18473             this.fireEvent("dblclick", this, this.indexOf(item), item, e);
18474         }
18475     },
18476
18477     onItemClick : function(item, index, e)
18478     {
18479         if(this.fireEvent("beforeclick", this, index, item, e) === false){
18480             return false;
18481         }
18482         if (this.toggleSelect) {
18483             var m = this.isSelected(item) ? 'unselect' : 'select';
18484             //Roo.log(m);
18485             var _t = this;
18486             _t[m](item, true, false);
18487             return true;
18488         }
18489         if(this.multiSelect || this.singleSelect){
18490             if(this.multiSelect && e.shiftKey && this.lastSelection){
18491                 this.select(this.getNodes(this.indexOf(this.lastSelection), index), false);
18492             }else{
18493                 this.select(item, this.multiSelect && e.ctrlKey);
18494                 this.lastSelection = item;
18495             }
18496             
18497             if(!this.tickable){
18498                 e.preventDefault();
18499             }
18500             
18501         }
18502         return true;
18503     },
18504
18505     /**
18506      * Get the number of selected nodes.
18507      * @return {Number}
18508      */
18509     getSelectionCount : function(){
18510         return this.selections.length;
18511     },
18512
18513     /**
18514      * Get the currently selected nodes.
18515      * @return {Array} An array of HTMLElements
18516      */
18517     getSelectedNodes : function(){
18518         return this.selections;
18519     },
18520
18521     /**
18522      * Get the indexes of the selected nodes.
18523      * @return {Array}
18524      */
18525     getSelectedIndexes : function(){
18526         var indexes = [], s = this.selections;
18527         for(var i = 0, len = s.length; i < len; i++){
18528             indexes.push(s[i].nodeIndex);
18529         }
18530         return indexes;
18531     },
18532
18533     /**
18534      * Clear all selections
18535      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange event
18536      */
18537     clearSelections : function(suppressEvent){
18538         if(this.nodes && (this.multiSelect || this.singleSelect) && this.selections.length > 0){
18539             this.cmp.elements = this.selections;
18540             this.cmp.removeClass(this.selectedClass);
18541             this.selections = [];
18542             if(!suppressEvent){
18543                 this.fireEvent("selectionchange", this, this.selections);
18544             }
18545         }
18546     },
18547
18548     /**
18549      * Returns true if the passed node is selected
18550      * @param {HTMLElement/Number} node The node or node index
18551      * @return {Boolean}
18552      */
18553     isSelected : function(node){
18554         var s = this.selections;
18555         if(s.length < 1){
18556             return false;
18557         }
18558         node = this.getNode(node);
18559         return s.indexOf(node) !== -1;
18560     },
18561
18562     /**
18563      * Selects nodes.
18564      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18565      * @param {Boolean} keepExisting (optional) true to keep existing selections
18566      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18567      */
18568     select : function(nodeInfo, keepExisting, suppressEvent){
18569         if(nodeInfo instanceof Array){
18570             if(!keepExisting){
18571                 this.clearSelections(true);
18572             }
18573             for(var i = 0, len = nodeInfo.length; i < len; i++){
18574                 this.select(nodeInfo[i], true, true);
18575             }
18576             return;
18577         } 
18578         var node = this.getNode(nodeInfo);
18579         if(!node || this.isSelected(node)){
18580             return; // already selected.
18581         }
18582         if(!keepExisting){
18583             this.clearSelections(true);
18584         }
18585         
18586         if(this.fireEvent("beforeselect", this, node, this.selections) !== false){
18587             Roo.fly(node).addClass(this.selectedClass);
18588             this.selections.push(node);
18589             if(!suppressEvent){
18590                 this.fireEvent("selectionchange", this, this.selections);
18591             }
18592         }
18593         
18594         
18595     },
18596       /**
18597      * Unselects nodes.
18598      * @param {Array/HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node, id of a template node or an array of any of those to select
18599      * @param {Boolean} keepExisting (optional) true IGNORED (for campatibility with select)
18600      * @param {Boolean} suppressEvent (optional) true to skip firing of the selectionchange vent
18601      */
18602     unselect : function(nodeInfo, keepExisting, suppressEvent)
18603     {
18604         if(nodeInfo instanceof Array){
18605             Roo.each(this.selections, function(s) {
18606                 this.unselect(s, nodeInfo);
18607             }, this);
18608             return;
18609         }
18610         var node = this.getNode(nodeInfo);
18611         if(!node || !this.isSelected(node)){
18612             //Roo.log("not selected");
18613             return; // not selected.
18614         }
18615         // fireevent???
18616         var ns = [];
18617         Roo.each(this.selections, function(s) {
18618             if (s == node ) {
18619                 Roo.fly(node).removeClass(this.selectedClass);
18620
18621                 return;
18622             }
18623             ns.push(s);
18624         },this);
18625         
18626         this.selections= ns;
18627         this.fireEvent("selectionchange", this, this.selections);
18628     },
18629
18630     /**
18631      * Gets a template node.
18632      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18633      * @return {HTMLElement} The node or null if it wasn't found
18634      */
18635     getNode : function(nodeInfo){
18636         if(typeof nodeInfo == "string"){
18637             return document.getElementById(nodeInfo);
18638         }else if(typeof nodeInfo == "number"){
18639             return this.nodes[nodeInfo];
18640         }
18641         return nodeInfo;
18642     },
18643
18644     /**
18645      * Gets a range template nodes.
18646      * @param {Number} startIndex
18647      * @param {Number} endIndex
18648      * @return {Array} An array of nodes
18649      */
18650     getNodes : function(start, end){
18651         var ns = this.nodes;
18652         start = start || 0;
18653         end = typeof end == "undefined" ? ns.length - 1 : end;
18654         var nodes = [];
18655         if(start <= end){
18656             for(var i = start; i <= end; i++){
18657                 nodes.push(ns[i]);
18658             }
18659         } else{
18660             for(var i = start; i >= end; i--){
18661                 nodes.push(ns[i]);
18662             }
18663         }
18664         return nodes;
18665     },
18666
18667     /**
18668      * Finds the index of the passed node
18669      * @param {HTMLElement/String/Number} nodeInfo An HTMLElement template node, index of a template node or the id of a template node
18670      * @return {Number} The index of the node or -1
18671      */
18672     indexOf : function(node){
18673         node = this.getNode(node);
18674         if(typeof node.nodeIndex == "number"){
18675             return node.nodeIndex;
18676         }
18677         var ns = this.nodes;
18678         for(var i = 0, len = ns.length; i < len; i++){
18679             if(ns[i] == node){
18680                 return i;
18681             }
18682         }
18683         return -1;
18684     }
18685 });
18686 /*
18687  * - LGPL
18688  *
18689  * based on jquery fullcalendar
18690  * 
18691  */
18692
18693 Roo.bootstrap = Roo.bootstrap || {};
18694 /**
18695  * @class Roo.bootstrap.Calendar
18696  * @extends Roo.bootstrap.Component
18697  * Bootstrap Calendar class
18698  * @cfg {Boolean} loadMask (true|false) default false
18699  * @cfg {Object} header generate the user specific header of the calendar, default false
18700
18701  * @constructor
18702  * Create a new Container
18703  * @param {Object} config The config object
18704  */
18705
18706
18707
18708 Roo.bootstrap.Calendar = function(config){
18709     Roo.bootstrap.Calendar.superclass.constructor.call(this, config);
18710      this.addEvents({
18711         /**
18712              * @event select
18713              * Fires when a date is selected
18714              * @param {DatePicker} this
18715              * @param {Date} date The selected date
18716              */
18717         'select': true,
18718         /**
18719              * @event monthchange
18720              * Fires when the displayed month changes 
18721              * @param {DatePicker} this
18722              * @param {Date} date The selected month
18723              */
18724         'monthchange': true,
18725         /**
18726              * @event evententer
18727              * Fires when mouse over an event
18728              * @param {Calendar} this
18729              * @param {event} Event
18730              */
18731         'evententer': true,
18732         /**
18733              * @event eventleave
18734              * Fires when the mouse leaves an
18735              * @param {Calendar} this
18736              * @param {event}
18737              */
18738         'eventleave': true,
18739         /**
18740              * @event eventclick
18741              * Fires when the mouse click an
18742              * @param {Calendar} this
18743              * @param {event}
18744              */
18745         'eventclick': true
18746         
18747     });
18748
18749 };
18750
18751 Roo.extend(Roo.bootstrap.Calendar, Roo.bootstrap.Component,  {
18752     
18753      /**
18754      * @cfg {Number} startDay
18755      * Day index at which the week should begin, 0-based (defaults to 0, which is Sunday)
18756      */
18757     startDay : 0,
18758     
18759     loadMask : false,
18760     
18761     header : false,
18762       
18763     getAutoCreate : function(){
18764         
18765         
18766         var fc_button = function(name, corner, style, content ) {
18767             return Roo.apply({},{
18768                 tag : 'span',
18769                 cls : 'fc-button fc-button-'+name+' fc-state-default ' + 
18770                          (corner.length ?
18771                             'fc-corner-' + corner.split(' ').join(' fc-corner-') :
18772                             ''
18773                         ),
18774                 html : '<SPAN class="fc-text-'+style+ '">'+content +'</SPAN>',
18775                 unselectable: 'on'
18776             });
18777         };
18778         
18779         var header = {};
18780         
18781         if(!this.header){
18782             header = {
18783                 tag : 'table',
18784                 cls : 'fc-header',
18785                 style : 'width:100%',
18786                 cn : [
18787                     {
18788                         tag: 'tr',
18789                         cn : [
18790                             {
18791                                 tag : 'td',
18792                                 cls : 'fc-header-left',
18793                                 cn : [
18794                                     fc_button('prev', 'left', 'arrow', '&#8249;' ),
18795                                     fc_button('next', 'right', 'arrow', '&#8250;' ),
18796                                     { tag: 'span', cls: 'fc-header-space' },
18797                                     fc_button('today', 'left right', '', 'today' )  // neds state disabled..
18798
18799
18800                                 ]
18801                             },
18802
18803                             {
18804                                 tag : 'td',
18805                                 cls : 'fc-header-center',
18806                                 cn : [
18807                                     {
18808                                         tag: 'span',
18809                                         cls: 'fc-header-title',
18810                                         cn : {
18811                                             tag: 'H2',
18812                                             html : 'month / year'
18813                                         }
18814                                     }
18815
18816                                 ]
18817                             },
18818                             {
18819                                 tag : 'td',
18820                                 cls : 'fc-header-right',
18821                                 cn : [
18822                               /*      fc_button('month', 'left', '', 'month' ),
18823                                     fc_button('week', '', '', 'week' ),
18824                                     fc_button('day', 'right', '', 'day' )
18825                                 */    
18826
18827                                 ]
18828                             }
18829
18830                         ]
18831                     }
18832                 ]
18833             };
18834         }
18835         
18836         header = this.header;
18837         
18838        
18839         var cal_heads = function() {
18840             var ret = [];
18841             // fixme - handle this.
18842             
18843             for (var i =0; i < Date.dayNames.length; i++) {
18844                 var d = Date.dayNames[i];
18845                 ret.push({
18846                     tag: 'th',
18847                     cls : 'fc-day-header fc-' + d.substring(0,3).toLowerCase() + ' fc-widget-header',
18848                     html : d.substring(0,3)
18849                 });
18850                 
18851             }
18852             ret[0].cls += ' fc-first';
18853             ret[6].cls += ' fc-last';
18854             return ret;
18855         };
18856         var cal_cell = function(n) {
18857             return  {
18858                 tag: 'td',
18859                 cls : 'fc-day fc-'+n + ' fc-widget-content', ///fc-other-month fc-past
18860                 cn : [
18861                     {
18862                         cn : [
18863                             {
18864                                 cls: 'fc-day-number',
18865                                 html: 'D'
18866                             },
18867                             {
18868                                 cls: 'fc-day-content',
18869                              
18870                                 cn : [
18871                                      {
18872                                         style: 'position: relative;' // height: 17px;
18873                                     }
18874                                 ]
18875                             }
18876                             
18877                             
18878                         ]
18879                     }
18880                 ]
18881                 
18882             }
18883         };
18884         var cal_rows = function() {
18885             
18886             var ret = [];
18887             for (var r = 0; r < 6; r++) {
18888                 var row= {
18889                     tag : 'tr',
18890                     cls : 'fc-week',
18891                     cn : []
18892                 };
18893                 
18894                 for (var i =0; i < Date.dayNames.length; i++) {
18895                     var d = Date.dayNames[i];
18896                     row.cn.push(cal_cell(d.substring(0,3).toLowerCase()));
18897
18898                 }
18899                 row.cn[0].cls+=' fc-first';
18900                 row.cn[0].cn[0].style = 'min-height:90px';
18901                 row.cn[6].cls+=' fc-last';
18902                 ret.push(row);
18903                 
18904             }
18905             ret[0].cls += ' fc-first';
18906             ret[4].cls += ' fc-prev-last';
18907             ret[5].cls += ' fc-last';
18908             return ret;
18909             
18910         };
18911         
18912         var cal_table = {
18913             tag: 'table',
18914             cls: 'fc-border-separate',
18915             style : 'width:100%',
18916             cellspacing  : 0,
18917             cn : [
18918                 { 
18919                     tag: 'thead',
18920                     cn : [
18921                         { 
18922                             tag: 'tr',
18923                             cls : 'fc-first fc-last',
18924                             cn : cal_heads()
18925                         }
18926                     ]
18927                 },
18928                 { 
18929                     tag: 'tbody',
18930                     cn : cal_rows()
18931                 }
18932                   
18933             ]
18934         };
18935          
18936          var cfg = {
18937             cls : 'fc fc-ltr',
18938             cn : [
18939                 header,
18940                 {
18941                     cls : 'fc-content',
18942                     style : "position: relative;",
18943                     cn : [
18944                         {
18945                             cls : 'fc-view fc-view-month fc-grid',
18946                             style : 'position: relative',
18947                             unselectable : 'on',
18948                             cn : [
18949                                 {
18950                                     cls : 'fc-event-container',
18951                                     style : 'position:absolute;z-index:8;top:0;left:0;'
18952                                 },
18953                                 cal_table
18954                             ]
18955                         }
18956                     ]
18957     
18958                 }
18959            ] 
18960             
18961         };
18962         
18963          
18964         
18965         return cfg;
18966     },
18967     
18968     
18969     initEvents : function()
18970     {
18971         if(!this.store){
18972             throw "can not find store for calendar";
18973         }
18974         
18975         var mark = {
18976             tag: "div",
18977             cls:"x-dlg-mask",
18978             style: "text-align:center",
18979             cn: [
18980                 {
18981                     tag: "div",
18982                     style: "background-color:white;width:50%;margin:250 auto",
18983                     cn: [
18984                         {
18985                             tag: "img",
18986                             src: Roo.rootURL + '/images/ux/lightbox/loading.gif' 
18987                         },
18988                         {
18989                             tag: "span",
18990                             html: "Loading"
18991                         }
18992                         
18993                     ]
18994                 }
18995             ]
18996         };
18997         this.maskEl = Roo.DomHelper.append(this.el.select('.fc-content', true).first(), mark, true);
18998         
18999         var size = this.el.select('.fc-content', true).first().getSize();
19000         this.maskEl.setSize(size.width, size.height);
19001         this.maskEl.enableDisplayMode("block");
19002         if(!this.loadMask){
19003             this.maskEl.hide();
19004         }
19005         
19006         this.store = Roo.factory(this.store, Roo.data);
19007         this.store.on('load', this.onLoad, this);
19008         this.store.on('beforeload', this.onBeforeLoad, this);
19009         
19010         this.resize();
19011         
19012         this.cells = this.el.select('.fc-day',true);
19013         //Roo.log(this.cells);
19014         this.textNodes = this.el.query('.fc-day-number');
19015         this.cells.addClassOnOver('fc-state-hover');
19016         
19017         this.el.select('.fc-button-prev',true).on('click', this.showPrevMonth, this);
19018         this.el.select('.fc-button-next',true).on('click', this.showNextMonth, this);
19019         this.el.select('.fc-button-today',true).on('click', this.showToday, this);
19020         this.el.select('.fc-button',true).addClassOnOver('fc-state-hover');
19021         
19022         this.on('monthchange', this.onMonthChange, this);
19023         
19024         this.update(new Date().clearTime());
19025     },
19026     
19027     resize : function() {
19028         var sz  = this.el.getSize();
19029         
19030         this.el.select('.fc-day-header',true).setWidth(sz.width / 7);
19031         this.el.select('.fc-day-content div',true).setHeight(34);
19032     },
19033     
19034     
19035     // private
19036     showPrevMonth : function(e){
19037         this.update(this.activeDate.add("mo", -1));
19038     },
19039     showToday : function(e){
19040         this.update(new Date().clearTime());
19041     },
19042     // private
19043     showNextMonth : function(e){
19044         this.update(this.activeDate.add("mo", 1));
19045     },
19046
19047     // private
19048     showPrevYear : function(){
19049         this.update(this.activeDate.add("y", -1));
19050     },
19051
19052     // private
19053     showNextYear : function(){
19054         this.update(this.activeDate.add("y", 1));
19055     },
19056
19057     
19058    // private
19059     update : function(date)
19060     {
19061         var vd = this.activeDate;
19062         this.activeDate = date;
19063 //        if(vd && this.el){
19064 //            var t = date.getTime();
19065 //            if(vd.getMonth() == date.getMonth() && vd.getFullYear() == date.getFullYear()){
19066 //                Roo.log('using add remove');
19067 //                
19068 //                this.fireEvent('monthchange', this, date);
19069 //                
19070 //                this.cells.removeClass("fc-state-highlight");
19071 //                this.cells.each(function(c){
19072 //                   if(c.dateValue == t){
19073 //                       c.addClass("fc-state-highlight");
19074 //                       setTimeout(function(){
19075 //                            try{c.dom.firstChild.focus();}catch(e){}
19076 //                       }, 50);
19077 //                       return false;
19078 //                   }
19079 //                   return true;
19080 //                });
19081 //                return;
19082 //            }
19083 //        }
19084         
19085         var days = date.getDaysInMonth();
19086         
19087         var firstOfMonth = date.getFirstDateOfMonth();
19088         var startingPos = firstOfMonth.getDay()-this.startDay;
19089         
19090         if(startingPos < this.startDay){
19091             startingPos += 7;
19092         }
19093         
19094         var pm = date.add(Date.MONTH, -1);
19095         var prevStart = pm.getDaysInMonth()-startingPos;
19096 //        
19097         this.cells = this.el.select('.fc-day',true);
19098         this.textNodes = this.el.query('.fc-day-number');
19099         this.cells.addClassOnOver('fc-state-hover');
19100         
19101         var cells = this.cells.elements;
19102         var textEls = this.textNodes;
19103         
19104         Roo.each(cells, function(cell){
19105             cell.removeClass([ 'fc-past', 'fc-other-month', 'fc-future', 'fc-state-highlight', 'fc-state-disabled']);
19106         });
19107         
19108         days += startingPos;
19109
19110         // convert everything to numbers so it's fast
19111         var day = 86400000;
19112         var d = (new Date(pm.getFullYear(), pm.getMonth(), prevStart)).clearTime();
19113         //Roo.log(d);
19114         //Roo.log(pm);
19115         //Roo.log(prevStart);
19116         
19117         var today = new Date().clearTime().getTime();
19118         var sel = date.clearTime().getTime();
19119         var min = this.minDate ? this.minDate.clearTime() : Number.NEGATIVE_INFINITY;
19120         var max = this.maxDate ? this.maxDate.clearTime() : Number.POSITIVE_INFINITY;
19121         var ddMatch = this.disabledDatesRE;
19122         var ddText = this.disabledDatesText;
19123         var ddays = this.disabledDays ? this.disabledDays.join("") : false;
19124         var ddaysText = this.disabledDaysText;
19125         var format = this.format;
19126         
19127         var setCellClass = function(cal, cell){
19128             cell.row = 0;
19129             cell.events = [];
19130             cell.more = [];
19131             //Roo.log('set Cell Class');
19132             cell.title = "";
19133             var t = d.getTime();
19134             
19135             //Roo.log(d);
19136             
19137             cell.dateValue = t;
19138             if(t == today){
19139                 cell.className += " fc-today";
19140                 cell.className += " fc-state-highlight";
19141                 cell.title = cal.todayText;
19142             }
19143             if(t == sel){
19144                 // disable highlight in other month..
19145                 //cell.className += " fc-state-highlight";
19146                 
19147             }
19148             // disabling
19149             if(t < min) {
19150                 cell.className = " fc-state-disabled";
19151                 cell.title = cal.minText;
19152                 return;
19153             }
19154             if(t > max) {
19155                 cell.className = " fc-state-disabled";
19156                 cell.title = cal.maxText;
19157                 return;
19158             }
19159             if(ddays){
19160                 if(ddays.indexOf(d.getDay()) != -1){
19161                     cell.title = ddaysText;
19162                     cell.className = " fc-state-disabled";
19163                 }
19164             }
19165             if(ddMatch && format){
19166                 var fvalue = d.dateFormat(format);
19167                 if(ddMatch.test(fvalue)){
19168                     cell.title = ddText.replace("%0", fvalue);
19169                     cell.className = " fc-state-disabled";
19170                 }
19171             }
19172             
19173             if (!cell.initialClassName) {
19174                 cell.initialClassName = cell.dom.className;
19175             }
19176             
19177             cell.dom.className = cell.initialClassName  + ' ' +  cell.className;
19178         };
19179
19180         var i = 0;
19181         
19182         for(; i < startingPos; i++) {
19183             textEls[i].innerHTML = (++prevStart);
19184             d.setDate(d.getDate()+1);
19185             
19186             cells[i].className = "fc-past fc-other-month";
19187             setCellClass(this, cells[i]);
19188         }
19189         
19190         var intDay = 0;
19191         
19192         for(; i < days; i++){
19193             intDay = i - startingPos + 1;
19194             textEls[i].innerHTML = (intDay);
19195             d.setDate(d.getDate()+1);
19196             
19197             cells[i].className = ''; // "x-date-active";
19198             setCellClass(this, cells[i]);
19199         }
19200         var extraDays = 0;
19201         
19202         for(; i < 42; i++) {
19203             textEls[i].innerHTML = (++extraDays);
19204             d.setDate(d.getDate()+1);
19205             
19206             cells[i].className = "fc-future fc-other-month";
19207             setCellClass(this, cells[i]);
19208         }
19209         
19210         this.el.select('.fc-header-title h2',true).update(Date.monthNames[date.getMonth()] + " " + date.getFullYear());
19211         
19212         var totalRows = Math.ceil((date.getDaysInMonth() + date.getFirstDateOfMonth().getDay()) / 7);
19213         
19214         this.el.select('tr.fc-week.fc-prev-last',true).removeClass('fc-last');
19215         this.el.select('tr.fc-week.fc-next-last',true).addClass('fc-last').show();
19216         
19217         if(totalRows != 6){
19218             this.el.select('tr.fc-week.fc-last',true).removeClass('fc-last').addClass('fc-next-last').hide();
19219             this.el.select('tr.fc-week.fc-prev-last',true).addClass('fc-last');
19220         }
19221         
19222         this.fireEvent('monthchange', this, date);
19223         
19224         
19225         /*
19226         if(!this.internalRender){
19227             var main = this.el.dom.firstChild;
19228             var w = main.offsetWidth;
19229             this.el.setWidth(w + this.el.getBorderWidth("lr"));
19230             Roo.fly(main).setWidth(w);
19231             this.internalRender = true;
19232             // opera does not respect the auto grow header center column
19233             // then, after it gets a width opera refuses to recalculate
19234             // without a second pass
19235             if(Roo.isOpera && !this.secondPass){
19236                 main.rows[0].cells[1].style.width = (w - (main.rows[0].cells[0].offsetWidth+main.rows[0].cells[2].offsetWidth)) + "px";
19237                 this.secondPass = true;
19238                 this.update.defer(10, this, [date]);
19239             }
19240         }
19241         */
19242         
19243     },
19244     
19245     findCell : function(dt) {
19246         dt = dt.clearTime().getTime();
19247         var ret = false;
19248         this.cells.each(function(c){
19249             //Roo.log("check " +c.dateValue + '?=' + dt);
19250             if(c.dateValue == dt){
19251                 ret = c;
19252                 return false;
19253             }
19254             return true;
19255         });
19256         
19257         return ret;
19258     },
19259     
19260     findCells : function(ev) {
19261         var s = ev.start.clone().clearTime().getTime();
19262        // Roo.log(s);
19263         var e= ev.end.clone().clearTime().getTime();
19264        // Roo.log(e);
19265         var ret = [];
19266         this.cells.each(function(c){
19267              ////Roo.log("check " +c.dateValue + '<' + e + ' > ' + s);
19268             
19269             if(c.dateValue > e){
19270                 return ;
19271             }
19272             if(c.dateValue < s){
19273                 return ;
19274             }
19275             ret.push(c);
19276         });
19277         
19278         return ret;    
19279     },
19280     
19281 //    findBestRow: function(cells)
19282 //    {
19283 //        var ret = 0;
19284 //        
19285 //        for (var i =0 ; i < cells.length;i++) {
19286 //            ret  = Math.max(cells[i].rows || 0,ret);
19287 //        }
19288 //        return ret;
19289 //        
19290 //    },
19291     
19292     
19293     addItem : function(ev)
19294     {
19295         // look for vertical location slot in
19296         var cells = this.findCells(ev);
19297         
19298 //        ev.row = this.findBestRow(cells);
19299         
19300         // work out the location.
19301         
19302         var crow = false;
19303         var rows = [];
19304         for(var i =0; i < cells.length; i++) {
19305             
19306             cells[i].row = cells[0].row;
19307             
19308             if(i == 0){
19309                 cells[i].row = cells[i].row + 1;
19310             }
19311             
19312             if (!crow) {
19313                 crow = {
19314                     start : cells[i],
19315                     end :  cells[i]
19316                 };
19317                 continue;
19318             }
19319             if (crow.start.getY() == cells[i].getY()) {
19320                 // on same row.
19321                 crow.end = cells[i];
19322                 continue;
19323             }
19324             // different row.
19325             rows.push(crow);
19326             crow = {
19327                 start: cells[i],
19328                 end : cells[i]
19329             };
19330             
19331         }
19332         
19333         rows.push(crow);
19334         ev.els = [];
19335         ev.rows = rows;
19336         ev.cells = cells;
19337         
19338         cells[0].events.push(ev);
19339         
19340         this.calevents.push(ev);
19341     },
19342     
19343     clearEvents: function() {
19344         
19345         if(!this.calevents){
19346             return;
19347         }
19348         
19349         Roo.each(this.cells.elements, function(c){
19350             c.row = 0;
19351             c.events = [];
19352             c.more = [];
19353         });
19354         
19355         Roo.each(this.calevents, function(e) {
19356             Roo.each(e.els, function(el) {
19357                 el.un('mouseenter' ,this.onEventEnter, this);
19358                 el.un('mouseleave' ,this.onEventLeave, this);
19359                 el.remove();
19360             },this);
19361         },this);
19362         
19363         Roo.each(Roo.select('.fc-more-event', true).elements, function(e){
19364             e.remove();
19365         });
19366         
19367     },
19368     
19369     renderEvents: function()
19370     {   
19371         var _this = this;
19372         
19373         this.cells.each(function(c) {
19374             
19375             if(c.row < 5){
19376                 return;
19377             }
19378             
19379             var ev = c.events;
19380             
19381             var r = 4;
19382             if(c.row != c.events.length){
19383                 r = 4 - (4 - (c.row - c.events.length));
19384             }
19385             
19386             c.events = ev.slice(0, r);
19387             c.more = ev.slice(r);
19388             
19389             if(c.more.length && c.more.length == 1){
19390                 c.events.push(c.more.pop());
19391             }
19392             
19393             c.row = (c.row - ev.length) + c.events.length + ((c.more.length) ? 1 : 0);
19394             
19395         });
19396             
19397         this.cells.each(function(c) {
19398             
19399             c.select('.fc-day-content div',true).first().setHeight(Math.max(34, c.row * 20));
19400             
19401             
19402             for (var e = 0; e < c.events.length; e++){
19403                 var ev = c.events[e];
19404                 var rows = ev.rows;
19405                 
19406                 for(var i = 0; i < rows.length; i++) {
19407                 
19408                     // how many rows should it span..
19409
19410                     var  cfg = {
19411                         cls : 'roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable',
19412                         style : 'position: absolute', // left: 387px; width: 121px; top: 359px;
19413
19414                         unselectable : "on",
19415                         cn : [
19416                             {
19417                                 cls: 'fc-event-inner',
19418                                 cn : [
19419     //                                {
19420     //                                  tag:'span',
19421     //                                  cls: 'fc-event-time',
19422     //                                  html : cells.length > 1 ? '' : ev.time
19423     //                                },
19424                                     {
19425                                       tag:'span',
19426                                       cls: 'fc-event-title',
19427                                       html : String.format('{0}', ev.title)
19428                                     }
19429
19430
19431                                 ]
19432                             },
19433                             {
19434                                 cls: 'ui-resizable-handle ui-resizable-e',
19435                                 html : '&nbsp;&nbsp;&nbsp'
19436                             }
19437
19438                         ]
19439                     };
19440
19441                     if (i == 0) {
19442                         cfg.cls += ' fc-event-start';
19443                     }
19444                     if ((i+1) == rows.length) {
19445                         cfg.cls += ' fc-event-end';
19446                     }
19447
19448                     var ctr = _this.el.select('.fc-event-container',true).first();
19449                     var cg = ctr.createChild(cfg);
19450
19451                     var sbox = rows[i].start.select('.fc-day-content',true).first().getBox();
19452                     var ebox = rows[i].end.select('.fc-day-content',true).first().getBox();
19453
19454                     var r = (c.more.length) ? 1 : 0;
19455                     cg.setXY([sbox.x +2, sbox.y + ((c.row - c.events.length - r + e) * 20)]);    
19456                     cg.setWidth(ebox.right - sbox.x -2);
19457
19458                     cg.on('mouseenter' ,_this.onEventEnter, _this, ev);
19459                     cg.on('mouseleave' ,_this.onEventLeave, _this, ev);
19460                     cg.on('click', _this.onEventClick, _this, ev);
19461
19462                     ev.els.push(cg);
19463                     
19464                 }
19465                 
19466             }
19467             
19468             
19469             if(c.more.length){
19470                 var  cfg = {
19471                     cls : 'fc-more-event roo-dynamic fc-event fc-event-hori fc-event-draggable ui-draggable fc-event-start fc-event-end',
19472                     style : 'position: absolute',
19473                     unselectable : "on",
19474                     cn : [
19475                         {
19476                             cls: 'fc-event-inner',
19477                             cn : [
19478                                 {
19479                                   tag:'span',
19480                                   cls: 'fc-event-title',
19481                                   html : 'More'
19482                                 }
19483
19484
19485                             ]
19486                         },
19487                         {
19488                             cls: 'ui-resizable-handle ui-resizable-e',
19489                             html : '&nbsp;&nbsp;&nbsp'
19490                         }
19491
19492                     ]
19493                 };
19494
19495                 var ctr = _this.el.select('.fc-event-container',true).first();
19496                 var cg = ctr.createChild(cfg);
19497
19498                 var sbox = c.select('.fc-day-content',true).first().getBox();
19499                 var ebox = c.select('.fc-day-content',true).first().getBox();
19500                 //Roo.log(cg);
19501                 cg.setXY([sbox.x +2, sbox.y +((c.row - 1) * 20)]);    
19502                 cg.setWidth(ebox.right - sbox.x -2);
19503
19504                 cg.on('click', _this.onMoreEventClick, _this, c.more);
19505                 
19506             }
19507             
19508         });
19509         
19510         
19511         
19512     },
19513     
19514     onEventEnter: function (e, el,event,d) {
19515         this.fireEvent('evententer', this, el, event);
19516     },
19517     
19518     onEventLeave: function (e, el,event,d) {
19519         this.fireEvent('eventleave', this, el, event);
19520     },
19521     
19522     onEventClick: function (e, el,event,d) {
19523         this.fireEvent('eventclick', this, el, event);
19524     },
19525     
19526     onMonthChange: function () {
19527         this.store.load();
19528     },
19529     
19530     onMoreEventClick: function(e, el, more)
19531     {
19532         var _this = this;
19533         
19534         this.calpopover.placement = 'right';
19535         this.calpopover.setTitle('More');
19536         
19537         this.calpopover.setContent('');
19538         
19539         var ctr = this.calpopover.el.select('.popover-content', true).first();
19540         
19541         Roo.each(more, function(m){
19542             var cfg = {
19543                 cls : 'fc-event-hori fc-event-draggable',
19544                 html : m.title
19545             };
19546             var cg = ctr.createChild(cfg);
19547             
19548             cg.on('click', _this.onEventClick, _this, m);
19549         });
19550         
19551         this.calpopover.show(el);
19552         
19553         
19554     },
19555     
19556     onLoad: function () 
19557     {   
19558         this.calevents = [];
19559         var cal = this;
19560         
19561         if(this.store.getCount() > 0){
19562             this.store.data.each(function(d){
19563                cal.addItem({
19564                     id : d.data.id,
19565                     start: (typeof(d.data.start_dt) === 'string') ? new Date.parseDate(d.data.start_dt, 'Y-m-d H:i:s') : d.data.start_dt,
19566                     end : (typeof(d.data.end_dt) === 'string') ? new Date.parseDate(d.data.end_dt, 'Y-m-d H:i:s') : d.data.end_dt,
19567                     time : d.data.start_time,
19568                     title : d.data.title,
19569                     description : d.data.description,
19570                     venue : d.data.venue
19571                 });
19572             });
19573         }
19574         
19575         this.renderEvents();
19576         
19577         if(this.calevents.length && this.loadMask){
19578             this.maskEl.hide();
19579         }
19580     },
19581     
19582     onBeforeLoad: function()
19583     {
19584         this.clearEvents();
19585         if(this.loadMask){
19586             this.maskEl.show();
19587         }
19588     }
19589 });
19590
19591  
19592  /*
19593  * - LGPL
19594  *
19595  * element
19596  * 
19597  */
19598
19599 /**
19600  * @class Roo.bootstrap.Popover
19601  * @extends Roo.bootstrap.Component
19602  * Bootstrap Popover class
19603  * @cfg {String} html contents of the popover   (or false to use children..)
19604  * @cfg {String} title of popover (or false to hide)
19605  * @cfg {String|function} (right|top|bottom|left|auto) placement how it is placed
19606  * @cfg {String} trigger click || hover (or false to trigger manually)
19607  * @cfg {Boolean} modal - popovers that are modal will mask the screen, and must be closed with another event.
19608  * @cfg {String|Boolean|Roo.Element} add click hander to trigger show over what element
19609  *      - if false and it has a 'parent' then it will be automatically added to that element
19610  *      - if string - Roo.get  will be called 
19611  * @cfg {Number} delay - delay before showing
19612  
19613  * @constructor
19614  * Create a new Popover
19615  * @param {Object} config The config object
19616  */
19617
19618 Roo.bootstrap.Popover = function(config){
19619     Roo.bootstrap.Popover.superclass.constructor.call(this, config);
19620     
19621     this.addEvents({
19622         // raw events
19623          /**
19624          * @event show
19625          * After the popover show
19626          * 
19627          * @param {Roo.bootstrap.Popover} this
19628          */
19629         "show" : true,
19630         /**
19631          * @event hide
19632          * After the popover hide
19633          * 
19634          * @param {Roo.bootstrap.Popover} this
19635          */
19636         "hide" : true
19637     });
19638 };
19639
19640 Roo.extend(Roo.bootstrap.Popover, Roo.bootstrap.Component,  {
19641     
19642     title: false,
19643     html: false,
19644     
19645     placement : 'right',
19646     trigger : 'hover', // hover
19647     modal : false,
19648     delay : 0,
19649     
19650     over: false,
19651     
19652     can_build_overlaid : false,
19653     
19654     maskEl : false, // the mask element
19655     
19656     getChildContainer : function()
19657     {
19658         return this.contentEl;
19659         return this.el.select('.popover-content',true).first();
19660     },
19661     getPopoverHeader : function()
19662     {
19663         return this.headerEl
19664     }
19665     
19666     
19667     getAutoCreate : function(){
19668          
19669         var cfg = {
19670            cls : 'popover roo-dynamic shadow roo-popover' + (this.modal ? '-modal' : ''),
19671            style: 'display:block',
19672            cn : [
19673                 {
19674                     cls : 'arrow'
19675                 },
19676                 {
19677                     cls : 'popover-inner ',
19678                     cn : [
19679                         {
19680                             tag: 'h3',
19681                             cls: 'popover-title popover-header',
19682                             html : this.title || ''
19683                         },
19684                         {
19685                             cls : 'popover-content popover-body'  + this.cls,
19686                             html : this.html || ''
19687                         }
19688                     ]
19689                     
19690                 }
19691            ]
19692         };
19693         
19694         return cfg;
19695     },
19696     /**
19697      * @param {string} the title
19698      */
19699     setTitle: function(str)
19700     {
19701         this.title = str;
19702         if (this.el) {
19703             this.el.select('.popover-title',true).first().dom.innerHTML = str;
19704         }
19705         
19706     },
19707     /**
19708      * @param {string} the body content
19709      */
19710     setContent: function(str)
19711     {
19712         this.html = str;
19713         if (this.contentEl) {
19714             this.contentEl.dom.innerHTML = str;
19715         }
19716         
19717     },
19718     // as it get's added to the bottom of the page.
19719     onRender : function(ct, position)
19720     {
19721         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
19722         if(!this.el){
19723             var cfg = Roo.apply({},  this.getAutoCreate());
19724             cfg.id = Roo.id();
19725             
19726             if (this.cls) {
19727                 cfg.cls += ' ' + this.cls;
19728             }
19729             if (this.style) {
19730                 cfg.style = this.style;
19731             }
19732             //Roo.log("adding to ");
19733             this.el = Roo.get(document.body).createChild(cfg, position);
19734 //            Roo.log(this.el);
19735         }
19736         
19737         var nitems = [];
19738         if(typeof(this.items) != 'undefined'){
19739             var items = this.items;
19740             delete this.items;
19741
19742             for(var i =0;i < items.length;i++) {
19743                 nitems.push(this.addxtype(Roo.apply({}, items[i])));
19744             }
19745         }
19746
19747         this.items = nitems;
19748         
19749         this.maskEl = Roo.DomHelper.append(document.body, {tag: "div", cls:"x-dlg-mask"}, true);
19750         Roo.EventManager.onWindowResize(this.resizeMask, this, true);
19751         
19752         
19753         this.contentEl = this.el.select('.popover-content',true).first();
19754         this.headerEl =  this.el.select('.popover-header',true).first();
19755         
19756         this.initEvents();
19757     },
19758     
19759     resizeMask : function()
19760     {
19761         this.maskEl.setSize(
19762             Roo.lib.Dom.getViewWidth(true),
19763             Roo.lib.Dom.getViewHeight(true)
19764         );
19765     },
19766     
19767     initEvents : function()
19768     {
19769         
19770         if (!this.modal) { 
19771             Roo.bootstrap.Popover.register(this);
19772         }
19773          
19774         
19775         this.el.select('.popover-title',true).setVisibilityMode(Roo.Element.DISPLAY);
19776         this.el.enableDisplayMode('block');
19777         this.el.hide();
19778         if (this.over === false && !this.parent()) {
19779             return; 
19780         }
19781         if (this.triggers === false) {
19782             return;
19783         }
19784          
19785         // support parent
19786         var on_el = (this.over == 'parent' || this.over === false) ? this.parent().el : Roo.get(this.over);
19787         var triggers = this.trigger ? this.trigger.split(' ') : [];
19788         Roo.each(triggers, function(trigger) {
19789         
19790             if (trigger == 'click') {
19791                 on_el.on('click', this.toggle, this);
19792             } else if (trigger != 'manual') {
19793                 var eventIn  = trigger == 'hover' ? 'mouseenter' : 'focusin';
19794                 var eventOut = trigger == 'hover' ? 'mouseleave' : 'focusout';
19795       
19796                 on_el.on(eventIn  ,this.enter, this);
19797                 on_el.on(eventOut, this.leave, this);
19798             }
19799         }, this);
19800         
19801     },
19802     
19803     
19804     // private
19805     timeout : null,
19806     hoverState : null,
19807     
19808     toggle : function () {
19809         this.hoverState == 'in' ? this.leave() : this.enter();
19810     },
19811     
19812     enter : function () {
19813         
19814         clearTimeout(this.timeout);
19815     
19816         this.hoverState = 'in';
19817     
19818         if (!this.delay || !this.delay.show) {
19819             this.show();
19820             return;
19821         }
19822         var _t = this;
19823         this.timeout = setTimeout(function () {
19824             if (_t.hoverState == 'in') {
19825                 _t.show();
19826             }
19827         }, this.delay.show)
19828     },
19829     
19830     leave : function() {
19831         clearTimeout(this.timeout);
19832     
19833         this.hoverState = 'out';
19834     
19835         if (!this.delay || !this.delay.hide) {
19836             this.hide();
19837             return;
19838         }
19839         var _t = this;
19840         this.timeout = setTimeout(function () {
19841             if (_t.hoverState == 'out') {
19842                 _t.hide();
19843             }
19844         }, this.delay.hide)
19845     },
19846     /**
19847      * Show the popover
19848      * @param {Roo.Element|string|false} - element to align and point to.
19849      */
19850     show : function (on_el)
19851     {
19852         
19853         on_el = on_el || false; // default to false
19854         if (!on_el) {
19855             if (this.parent() && (this.over == 'parent' || (this.over === false))) {
19856                 on_el = this.parent().el;
19857             } else if (this.over) {
19858                 Roo.get(this.over);
19859             }
19860             
19861         }
19862         
19863         if (!this.el) {
19864             this.render(document.body);
19865         }
19866         
19867         
19868         this.el.removeClass([
19869             'fade','top','bottom', 'left', 'right','in',
19870             'bs-popover-top','bs-popover-bottom', 'bs-popover-left', 'bs-popover-right'
19871         ]);
19872         
19873         if (!this.title.length) {
19874             this.el.select('.popover-title',true).hide();
19875         }
19876         
19877         
19878         var placement = typeof this.placement == 'function' ?
19879             this.placement.call(this, this.el, on_el) :
19880             this.placement;
19881             
19882         /*
19883         var autoToken = /\s?auto?\s?/i;   /// not sure how this was supposed to work? right auto ? what?
19884         
19885         // I think  'auto right' - but 
19886         
19887         var autoPlace = autoToken.test(placement);
19888         if (autoPlace) {
19889             placement = placement.replace(autoToken, '') || 'top';
19890         }
19891         */
19892         
19893         
19894         this.el.show();
19895         this.el.dom.style.display='block';
19896         
19897         //this.el.appendTo(on_el);
19898         
19899         var p = this.getPosition();
19900         var box = this.el.getBox();
19901         
19902         
19903         var align = Roo.bootstrap.Popover.alignment[placement];
19904         this.el.addClass(align[2]);
19905
19906 //        Roo.log(align);
19907
19908         if (on_el) {
19909             this.el.alignTo(on_el, align[0],align[1]);
19910         } else {
19911             // this is usually just done by the builder = to show the popoup in the middle of the scren.
19912             var es = this.el.getSize();
19913             var x = Roo.lib.Dom.getViewWidth()/2;
19914             var y = Roo.lib.Dom.getViewHeight()/2;
19915             this.el.setXY([ x-(es.width/2),  y-(es.height/2)] );
19916             
19917         }
19918
19919         
19920         //var arrow = this.el.select('.arrow',true).first();
19921         //arrow.set(align[2], 
19922         
19923         this.el.addClass('in');
19924         
19925         
19926         if (this.el.hasClass('fade')) {
19927             // fade it?
19928         }
19929         
19930         this.hoverState = 'in';
19931         
19932         if (this.modal) {
19933             this.maskEl.setSize(Roo.lib.Dom.getViewWidth(true),   Roo.lib.Dom.getViewHeight(true));
19934             this.maskEl.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19935             this.maskEl.dom.style.display = 'block';
19936             this.maskEl.addClass('show');
19937         }
19938         this.el.setStyle('z-index', Roo.bootstrap.Popover.zIndex++);
19939
19940         
19941         
19942         this.fireEvent('show', this);
19943         
19944     },
19945     hide : function()
19946     {
19947         this.el.setXY([0,0]);
19948         this.el.removeClass('in');
19949         this.el.hide();
19950         this.hoverState = null;
19951         this.maskEl.hide(); // always..
19952         this.fireEvent('hide', this);
19953     }
19954     
19955 });
19956
19957
19958 Roo.apply(Roo.bootstrap.Popover, {
19959
19960     alignment : {
19961         'left' : ['r-l', [-10,0], 'left bs-popover-left'],
19962         'right' : ['l-br', [10,0], 'right bs-popover-right'],
19963         'bottom' : ['t-b', [0,10], 'top bs-popover-top'],
19964         'top' : [ 'b-t', [0,-10], 'bottom bs-popover-bottom']
19965     },
19966     
19967     zIndex : 20001,
19968
19969     clickHander : false,
19970     
19971
19972     onMouseDown : function(e)
19973     {
19974         if (!e.getTarget(".roo-popover")) {
19975             this.hideAll();
19976         }
19977          
19978     },
19979     
19980     popups : [],
19981     
19982     register : function(popup)
19983     {
19984         if (!Roo.bootstrap.Popover.clickHandler) {
19985             Roo.bootstrap.Popover.clickHandler = Roo.get(document).on("mousedown", Roo.bootstrap.Popover.onMouseDown, Roo.bootstrap.Popover);
19986         }
19987         // hide other popups.
19988         this.hideAll();
19989         this.popups.push(popup);
19990     },
19991     hideAll : function()
19992     {
19993         this.popups.forEach(function(p) {
19994             p.hide();
19995         });
19996     }
19997
19998 });/*
19999  * - LGPL
20000  *
20001  * Progress
20002  * 
20003  */
20004
20005 /**
20006  * @class Roo.bootstrap.Progress
20007  * @extends Roo.bootstrap.Component
20008  * Bootstrap Progress class
20009  * @cfg {Boolean} striped striped of the progress bar
20010  * @cfg {Boolean} active animated of the progress bar
20011  * 
20012  * 
20013  * @constructor
20014  * Create a new Progress
20015  * @param {Object} config The config object
20016  */
20017
20018 Roo.bootstrap.Progress = function(config){
20019     Roo.bootstrap.Progress.superclass.constructor.call(this, config);
20020 };
20021
20022 Roo.extend(Roo.bootstrap.Progress, Roo.bootstrap.Component,  {
20023     
20024     striped : false,
20025     active: false,
20026     
20027     getAutoCreate : function(){
20028         var cfg = {
20029             tag: 'div',
20030             cls: 'progress'
20031         };
20032         
20033         
20034         if(this.striped){
20035             cfg.cls += ' progress-striped';
20036         }
20037       
20038         if(this.active){
20039             cfg.cls += ' active';
20040         }
20041         
20042         
20043         return cfg;
20044     }
20045    
20046 });
20047
20048  
20049
20050  /*
20051  * - LGPL
20052  *
20053  * ProgressBar
20054  * 
20055  */
20056
20057 /**
20058  * @class Roo.bootstrap.ProgressBar
20059  * @extends Roo.bootstrap.Component
20060  * Bootstrap ProgressBar class
20061  * @cfg {Number} aria_valuenow aria-value now
20062  * @cfg {Number} aria_valuemin aria-value min
20063  * @cfg {Number} aria_valuemax aria-value max
20064  * @cfg {String} label label for the progress bar
20065  * @cfg {String} panel (success | info | warning | danger )
20066  * @cfg {String} role role of the progress bar
20067  * @cfg {String} sr_only text
20068  * 
20069  * 
20070  * @constructor
20071  * Create a new ProgressBar
20072  * @param {Object} config The config object
20073  */
20074
20075 Roo.bootstrap.ProgressBar = function(config){
20076     Roo.bootstrap.ProgressBar.superclass.constructor.call(this, config);
20077 };
20078
20079 Roo.extend(Roo.bootstrap.ProgressBar, Roo.bootstrap.Component,  {
20080     
20081     aria_valuenow : 0,
20082     aria_valuemin : 0,
20083     aria_valuemax : 100,
20084     label : false,
20085     panel : false,
20086     role : false,
20087     sr_only: false,
20088     
20089     getAutoCreate : function()
20090     {
20091         
20092         var cfg = {
20093             tag: 'div',
20094             cls: 'progress-bar',
20095             style: 'width:' + Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%'
20096         };
20097         
20098         if(this.sr_only){
20099             cfg.cn = {
20100                 tag: 'span',
20101                 cls: 'sr-only',
20102                 html: this.sr_only
20103             }
20104         }
20105         
20106         if(this.role){
20107             cfg.role = this.role;
20108         }
20109         
20110         if(this.aria_valuenow){
20111             cfg['aria-valuenow'] = this.aria_valuenow;
20112         }
20113         
20114         if(this.aria_valuemin){
20115             cfg['aria-valuemin'] = this.aria_valuemin;
20116         }
20117         
20118         if(this.aria_valuemax){
20119             cfg['aria-valuemax'] = this.aria_valuemax;
20120         }
20121         
20122         if(this.label && !this.sr_only){
20123             cfg.html = this.label;
20124         }
20125         
20126         if(this.panel){
20127             cfg.cls += ' progress-bar-' + this.panel;
20128         }
20129         
20130         return cfg;
20131     },
20132     
20133     update : function(aria_valuenow)
20134     {
20135         this.aria_valuenow = aria_valuenow;
20136         
20137         this.el.setStyle('width', Math.ceil((this.aria_valuenow / this.aria_valuemax) * 100) + '%');
20138     }
20139    
20140 });
20141
20142  
20143
20144  /*
20145  * - LGPL
20146  *
20147  * column
20148  * 
20149  */
20150
20151 /**
20152  * @class Roo.bootstrap.TabGroup
20153  * @extends Roo.bootstrap.Column
20154  * Bootstrap Column class
20155  * @cfg {String} navId the navigation id (for use with navbars) - will be auto generated if it does not exist..
20156  * @cfg {Boolean} carousel true to make the group behave like a carousel
20157  * @cfg {Boolean} bullets show bullets for the panels
20158  * @cfg {Boolean} autoslide (true|false) auto slide .. default false
20159  * @cfg {Number} timer auto slide timer .. default 0 millisecond
20160  * @cfg {Boolean} showarrow (true|false) show arrow default true
20161  * 
20162  * @constructor
20163  * Create a new TabGroup
20164  * @param {Object} config The config object
20165  */
20166
20167 Roo.bootstrap.TabGroup = function(config){
20168     Roo.bootstrap.TabGroup.superclass.constructor.call(this, config);
20169     if (!this.navId) {
20170         this.navId = Roo.id();
20171     }
20172     this.tabs = [];
20173     Roo.bootstrap.TabGroup.register(this);
20174     
20175 };
20176
20177 Roo.extend(Roo.bootstrap.TabGroup, Roo.bootstrap.Column,  {
20178     
20179     carousel : false,
20180     transition : false,
20181     bullets : 0,
20182     timer : 0,
20183     autoslide : false,
20184     slideFn : false,
20185     slideOnTouch : false,
20186     showarrow : true,
20187     
20188     getAutoCreate : function()
20189     {
20190         var cfg = Roo.apply({}, Roo.bootstrap.TabGroup.superclass.getAutoCreate.call(this));
20191         
20192         cfg.cls += ' tab-content';
20193         
20194         if (this.carousel) {
20195             cfg.cls += ' carousel slide';
20196             
20197             cfg.cn = [{
20198                cls : 'carousel-inner',
20199                cn : []
20200             }];
20201         
20202             if(this.bullets  && !Roo.isTouch){
20203                 
20204                 var bullets = {
20205                     cls : 'carousel-bullets',
20206                     cn : []
20207                 };
20208                
20209                 if(this.bullets_cls){
20210                     bullets.cls = bullets.cls + ' ' + this.bullets_cls;
20211                 }
20212                 
20213                 bullets.cn.push({
20214                     cls : 'clear'
20215                 });
20216                 
20217                 cfg.cn[0].cn.push(bullets);
20218             }
20219             
20220             if(this.showarrow){
20221                 cfg.cn[0].cn.push({
20222                     tag : 'div',
20223                     class : 'carousel-arrow',
20224                     cn : [
20225                         {
20226                             tag : 'div',
20227                             class : 'carousel-prev',
20228                             cn : [
20229                                 {
20230                                     tag : 'i',
20231                                     class : 'fa fa-chevron-left'
20232                                 }
20233                             ]
20234                         },
20235                         {
20236                             tag : 'div',
20237                             class : 'carousel-next',
20238                             cn : [
20239                                 {
20240                                     tag : 'i',
20241                                     class : 'fa fa-chevron-right'
20242                                 }
20243                             ]
20244                         }
20245                     ]
20246                 });
20247             }
20248             
20249         }
20250         
20251         return cfg;
20252     },
20253     
20254     initEvents:  function()
20255     {
20256 //        if(Roo.isTouch && this.slideOnTouch && !this.showarrow){
20257 //            this.el.on("touchstart", this.onTouchStart, this);
20258 //        }
20259         
20260         if(this.autoslide){
20261             var _this = this;
20262             
20263             this.slideFn = window.setInterval(function() {
20264                 _this.showPanelNext();
20265             }, this.timer);
20266         }
20267         
20268         if(this.showarrow){
20269             this.el.select('.carousel-prev', true).first().on('click', this.showPanelPrev, this);
20270             this.el.select('.carousel-next', true).first().on('click', this.showPanelNext, this);
20271         }
20272         
20273         
20274     },
20275     
20276 //    onTouchStart : function(e, el, o)
20277 //    {
20278 //        if(!this.slideOnTouch || !Roo.isTouch || Roo.get(e.getTarget()).hasClass('roo-button-text')){
20279 //            return;
20280 //        }
20281 //        
20282 //        this.showPanelNext();
20283 //    },
20284     
20285     
20286     getChildContainer : function()
20287     {
20288         return this.carousel ? this.el.select('.carousel-inner', true).first() : this.el;
20289     },
20290     
20291     /**
20292     * register a Navigation item
20293     * @param {Roo.bootstrap.NavItem} the navitem to add
20294     */
20295     register : function(item)
20296     {
20297         this.tabs.push( item);
20298         item.navId = this.navId; // not really needed..
20299         this.addBullet();
20300     
20301     },
20302     
20303     getActivePanel : function()
20304     {
20305         var r = false;
20306         Roo.each(this.tabs, function(t) {
20307             if (t.active) {
20308                 r = t;
20309                 return false;
20310             }
20311             return null;
20312         });
20313         return r;
20314         
20315     },
20316     getPanelByName : function(n)
20317     {
20318         var r = false;
20319         Roo.each(this.tabs, function(t) {
20320             if (t.tabId == n) {
20321                 r = t;
20322                 return false;
20323             }
20324             return null;
20325         });
20326         return r;
20327     },
20328     indexOfPanel : function(p)
20329     {
20330         var r = false;
20331         Roo.each(this.tabs, function(t,i) {
20332             if (t.tabId == p.tabId) {
20333                 r = i;
20334                 return false;
20335             }
20336             return null;
20337         });
20338         return r;
20339     },
20340     /**
20341      * show a specific panel
20342      * @param {Roo.bootstrap.TabPanel|number|string} panel to change to (use the tabId to specify a specific one)
20343      * @return {boolean} false if panel was not shown (invalid entry or beforedeactivate fails.)
20344      */
20345     showPanel : function (pan)
20346     {
20347         if(this.transition || typeof(pan) == 'undefined'){
20348             Roo.log("waiting for the transitionend");
20349             return false;
20350         }
20351         
20352         if (typeof(pan) == 'number') {
20353             pan = this.tabs[pan];
20354         }
20355         
20356         if (typeof(pan) == 'string') {
20357             pan = this.getPanelByName(pan);
20358         }
20359         
20360         var cur = this.getActivePanel();
20361         
20362         if(!pan || !cur){
20363             Roo.log('pan or acitve pan is undefined');
20364             return false;
20365         }
20366         
20367         if (pan.tabId == this.getActivePanel().tabId) {
20368             return true;
20369         }
20370         
20371         if (false === cur.fireEvent('beforedeactivate')) {
20372             return false;
20373         }
20374         
20375         if(this.bullets > 0 && !Roo.isTouch){
20376             this.setActiveBullet(this.indexOfPanel(pan));
20377         }
20378         
20379         if (this.carousel && typeof(Roo.get(document.body).dom.style.transition) != 'undefined') {
20380             
20381             //class="carousel-item carousel-item-next carousel-item-left"
20382             
20383             this.transition = true;
20384             var dir = this.indexOfPanel(pan) > this.indexOfPanel(cur)  ? 'next' : 'prev';
20385             var lr = dir == 'next' ? 'left' : 'right';
20386             pan.el.addClass(dir); // or prev
20387             pan.el.addClass('carousel-item-' + dir); // or prev
20388             pan.el.dom.offsetWidth; // find the offset with - causing a reflow?
20389             cur.el.addClass(lr); // or right
20390             pan.el.addClass(lr);
20391             cur.el.addClass('carousel-item-' +lr); // or right
20392             pan.el.addClass('carousel-item-' +lr);
20393             
20394             
20395             var _this = this;
20396             cur.el.on('transitionend', function() {
20397                 Roo.log("trans end?");
20398                 
20399                 pan.el.removeClass([lr,dir, 'carousel-item-' + lr, 'carousel-item-' + dir]);
20400                 pan.setActive(true);
20401                 
20402                 cur.el.removeClass([lr, 'carousel-item-' + lr]);
20403                 cur.setActive(false);
20404                 
20405                 _this.transition = false;
20406                 
20407             }, this, { single:  true } );
20408             
20409             return true;
20410         }
20411         
20412         cur.setActive(false);
20413         pan.setActive(true);
20414         
20415         return true;
20416         
20417     },
20418     showPanelNext : function()
20419     {
20420         var i = this.indexOfPanel(this.getActivePanel());
20421         
20422         if (i >= this.tabs.length - 1 && !this.autoslide) {
20423             return;
20424         }
20425         
20426         if (i >= this.tabs.length - 1 && this.autoslide) {
20427             i = -1;
20428         }
20429         
20430         this.showPanel(this.tabs[i+1]);
20431     },
20432     
20433     showPanelPrev : function()
20434     {
20435         var i = this.indexOfPanel(this.getActivePanel());
20436         
20437         if (i  < 1 && !this.autoslide) {
20438             return;
20439         }
20440         
20441         if (i < 1 && this.autoslide) {
20442             i = this.tabs.length;
20443         }
20444         
20445         this.showPanel(this.tabs[i-1]);
20446     },
20447     
20448     
20449     addBullet: function()
20450     {
20451         if(!this.bullets || Roo.isTouch){
20452             return;
20453         }
20454         var ctr = this.el.select('.carousel-bullets',true).first();
20455         var i = this.el.select('.carousel-bullets .bullet',true).getCount() ;
20456         var bullet = ctr.createChild({
20457             cls : 'bullet bullet-' + i
20458         },ctr.dom.lastChild);
20459         
20460         
20461         var _this = this;
20462         
20463         bullet.on('click', (function(e, el, o, ii, t){
20464
20465             e.preventDefault();
20466
20467             this.showPanel(ii);
20468
20469             if(this.autoslide && this.slideFn){
20470                 clearInterval(this.slideFn);
20471                 this.slideFn = window.setInterval(function() {
20472                     _this.showPanelNext();
20473                 }, this.timer);
20474             }
20475
20476         }).createDelegate(this, [i, bullet], true));
20477                 
20478         
20479     },
20480      
20481     setActiveBullet : function(i)
20482     {
20483         if(Roo.isTouch){
20484             return;
20485         }
20486         
20487         Roo.each(this.el.select('.bullet', true).elements, function(el){
20488             el.removeClass('selected');
20489         });
20490
20491         var bullet = this.el.select('.bullet-' + i, true).first();
20492         
20493         if(!bullet){
20494             return;
20495         }
20496         
20497         bullet.addClass('selected');
20498     }
20499     
20500     
20501   
20502 });
20503
20504  
20505
20506  
20507  
20508 Roo.apply(Roo.bootstrap.TabGroup, {
20509     
20510     groups: {},
20511      /**
20512     * register a Navigation Group
20513     * @param {Roo.bootstrap.NavGroup} the navgroup to add
20514     */
20515     register : function(navgrp)
20516     {
20517         this.groups[navgrp.navId] = navgrp;
20518         
20519     },
20520     /**
20521     * fetch a Navigation Group based on the navigation ID
20522     * if one does not exist , it will get created.
20523     * @param {string} the navgroup to add
20524     * @returns {Roo.bootstrap.NavGroup} the navgroup 
20525     */
20526     get: function(navId) {
20527         if (typeof(this.groups[navId]) == 'undefined') {
20528             this.register(new Roo.bootstrap.TabGroup({ navId : navId }));
20529         }
20530         return this.groups[navId] ;
20531     }
20532     
20533     
20534     
20535 });
20536
20537  /*
20538  * - LGPL
20539  *
20540  * TabPanel
20541  * 
20542  */
20543
20544 /**
20545  * @class Roo.bootstrap.TabPanel
20546  * @extends Roo.bootstrap.Component
20547  * Bootstrap TabPanel class
20548  * @cfg {Boolean} active panel active
20549  * @cfg {String} html panel content
20550  * @cfg {String} tabId  unique tab ID (will be autogenerated if not set. - used to match TabItem to Panel)
20551  * @cfg {String} navId The Roo.bootstrap.NavGroup which triggers show hide ()
20552  * @cfg {String} href click to link..
20553  * @cfg {Boolean} touchSlide if swiping slides tab to next panel (default off)
20554  * 
20555  * 
20556  * @constructor
20557  * Create a new TabPanel
20558  * @param {Object} config The config object
20559  */
20560
20561 Roo.bootstrap.TabPanel = function(config){
20562     Roo.bootstrap.TabPanel.superclass.constructor.call(this, config);
20563     this.addEvents({
20564         /**
20565              * @event changed
20566              * Fires when the active status changes
20567              * @param {Roo.bootstrap.TabPanel} this
20568              * @param {Boolean} state the new state
20569             
20570          */
20571         'changed': true,
20572         /**
20573              * @event beforedeactivate
20574              * Fires before a tab is de-activated - can be used to do validation on a form.
20575              * @param {Roo.bootstrap.TabPanel} this
20576              * @return {Boolean} false if there is an error
20577             
20578          */
20579         'beforedeactivate': true
20580      });
20581     
20582     this.tabId = this.tabId || Roo.id();
20583   
20584 };
20585
20586 Roo.extend(Roo.bootstrap.TabPanel, Roo.bootstrap.Component,  {
20587     
20588     active: false,
20589     html: false,
20590     tabId: false,
20591     navId : false,
20592     href : '',
20593     touchSlide : false,
20594     getAutoCreate : function(){
20595         
20596         
20597         var cfg = {
20598             tag: 'div',
20599             // item is needed for carousel - not sure if it has any effect otherwise
20600             cls: 'carousel-item tab-pane item' + ((this.href.length) ? ' clickable ' : ''),
20601             html: this.html || ''
20602         };
20603         
20604         if(this.active){
20605             cfg.cls += ' active';
20606         }
20607         
20608         if(this.tabId){
20609             cfg.tabId = this.tabId;
20610         }
20611         
20612         
20613         
20614         return cfg;
20615     },
20616     
20617     initEvents:  function()
20618     {
20619         var p = this.parent();
20620         
20621         this.navId = this.navId || p.navId;
20622         
20623         if (typeof(this.navId) != 'undefined') {
20624             // not really needed.. but just in case.. parent should be a NavGroup.
20625             var tg = Roo.bootstrap.TabGroup.get(this.navId);
20626             
20627             tg.register(this);
20628             
20629             var i = tg.tabs.length - 1;
20630             
20631             if(this.active && tg.bullets > 0 && i < tg.bullets){
20632                 tg.setActiveBullet(i);
20633             }
20634         }
20635         
20636         this.el.on('click', this.onClick, this);
20637         
20638         if(Roo.isTouch && this.touchSlide){
20639             this.el.on("touchstart", this.onTouchStart, this);
20640             this.el.on("touchmove", this.onTouchMove, this);
20641             this.el.on("touchend", this.onTouchEnd, this);
20642         }
20643         
20644     },
20645     
20646     onRender : function(ct, position)
20647     {
20648         Roo.bootstrap.TabPanel.superclass.onRender.call(this, ct, position);
20649     },
20650     
20651     setActive : function(state)
20652     {
20653         Roo.log("panel - set active " + this.tabId + "=" + state);
20654         
20655         this.active = state;
20656         if (!state) {
20657             this.el.removeClass('active');
20658             
20659         } else  if (!this.el.hasClass('active')) {
20660             this.el.addClass('active');
20661         }
20662         
20663         this.fireEvent('changed', this, state);
20664     },
20665     
20666     onClick : function(e)
20667     {
20668         e.preventDefault();
20669         
20670         if(!this.href.length){
20671             return;
20672         }
20673         
20674         window.location.href = this.href;
20675     },
20676     
20677     startX : 0,
20678     startY : 0,
20679     endX : 0,
20680     endY : 0,
20681     swiping : false,
20682     
20683     onTouchStart : function(e)
20684     {
20685         this.swiping = false;
20686         
20687         this.startX = e.browserEvent.touches[0].clientX;
20688         this.startY = e.browserEvent.touches[0].clientY;
20689     },
20690     
20691     onTouchMove : function(e)
20692     {
20693         this.swiping = true;
20694         
20695         this.endX = e.browserEvent.touches[0].clientX;
20696         this.endY = e.browserEvent.touches[0].clientY;
20697     },
20698     
20699     onTouchEnd : function(e)
20700     {
20701         if(!this.swiping){
20702             this.onClick(e);
20703             return;
20704         }
20705         
20706         var tabGroup = this.parent();
20707         
20708         if(this.endX > this.startX){ // swiping right
20709             tabGroup.showPanelPrev();
20710             return;
20711         }
20712         
20713         if(this.startX > this.endX){ // swiping left
20714             tabGroup.showPanelNext();
20715             return;
20716         }
20717     }
20718     
20719     
20720 });
20721  
20722
20723  
20724
20725  /*
20726  * - LGPL
20727  *
20728  * DateField
20729  * 
20730  */
20731
20732 /**
20733  * @class Roo.bootstrap.DateField
20734  * @extends Roo.bootstrap.Input
20735  * Bootstrap DateField class
20736  * @cfg {Number} weekStart default 0
20737  * @cfg {String} viewMode default empty, (months|years)
20738  * @cfg {String} minViewMode default empty, (months|years)
20739  * @cfg {Number} startDate default -Infinity
20740  * @cfg {Number} endDate default Infinity
20741  * @cfg {Boolean} todayHighlight default false
20742  * @cfg {Boolean} todayBtn default false
20743  * @cfg {Boolean} calendarWeeks default false
20744  * @cfg {Object} daysOfWeekDisabled default empty
20745  * @cfg {Boolean} singleMode default false (true | false)
20746  * 
20747  * @cfg {Boolean} keyboardNavigation default true
20748  * @cfg {String} language default en
20749  * 
20750  * @constructor
20751  * Create a new DateField
20752  * @param {Object} config The config object
20753  */
20754
20755 Roo.bootstrap.DateField = function(config){
20756     Roo.bootstrap.DateField.superclass.constructor.call(this, config);
20757      this.addEvents({
20758             /**
20759              * @event show
20760              * Fires when this field show.
20761              * @param {Roo.bootstrap.DateField} this
20762              * @param {Mixed} date The date value
20763              */
20764             show : true,
20765             /**
20766              * @event show
20767              * Fires when this field hide.
20768              * @param {Roo.bootstrap.DateField} this
20769              * @param {Mixed} date The date value
20770              */
20771             hide : true,
20772             /**
20773              * @event select
20774              * Fires when select a date.
20775              * @param {Roo.bootstrap.DateField} this
20776              * @param {Mixed} date The date value
20777              */
20778             select : true,
20779             /**
20780              * @event beforeselect
20781              * Fires when before select a date.
20782              * @param {Roo.bootstrap.DateField} this
20783              * @param {Mixed} date The date value
20784              */
20785             beforeselect : true
20786         });
20787 };
20788
20789 Roo.extend(Roo.bootstrap.DateField, Roo.bootstrap.Input,  {
20790     
20791     /**
20792      * @cfg {String} format
20793      * The default date format string which can be overriden for localization support.  The format must be
20794      * valid according to {@link Date#parseDate} (defaults to 'm/d/y').
20795      */
20796     format : "m/d/y",
20797     /**
20798      * @cfg {String} altFormats
20799      * Multiple date formats separated by "|" to try when parsing a user input value and it doesn't match the defined
20800      * format (defaults to 'm/d/Y|m-d-y|m-d-Y|m/d|m-d|d').
20801      */
20802     altFormats : "m/d/Y|m-d-y|m-d-Y|m/d|m-d|md|mdy|mdY|d",
20803     
20804     weekStart : 0,
20805     
20806     viewMode : '',
20807     
20808     minViewMode : '',
20809     
20810     todayHighlight : false,
20811     
20812     todayBtn: false,
20813     
20814     language: 'en',
20815     
20816     keyboardNavigation: true,
20817     
20818     calendarWeeks: false,
20819     
20820     startDate: -Infinity,
20821     
20822     endDate: Infinity,
20823     
20824     daysOfWeekDisabled: [],
20825     
20826     _events: [],
20827     
20828     singleMode : false,
20829     
20830     UTCDate: function()
20831     {
20832         return new Date(Date.UTC.apply(Date, arguments));
20833     },
20834     
20835     UTCToday: function()
20836     {
20837         var today = new Date();
20838         return this.UTCDate(today.getUTCFullYear(), today.getUTCMonth(), today.getUTCDate());
20839     },
20840     
20841     getDate: function() {
20842             var d = this.getUTCDate();
20843             return new Date(d.getTime() + (d.getTimezoneOffset()*60000));
20844     },
20845     
20846     getUTCDate: function() {
20847             return this.date;
20848     },
20849     
20850     setDate: function(d) {
20851             this.setUTCDate(new Date(d.getTime() - (d.getTimezoneOffset()*60000)));
20852     },
20853     
20854     setUTCDate: function(d) {
20855             this.date = d;
20856             this.setValue(this.formatDate(this.date));
20857     },
20858         
20859     onRender: function(ct, position)
20860     {
20861         
20862         Roo.bootstrap.DateField.superclass.onRender.call(this, ct, position);
20863         
20864         this.language = this.language || 'en';
20865         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : this.language.split('-')[0];
20866         this.language = this.language in Roo.bootstrap.DateField.dates ? this.language : "en";
20867         
20868         this.isRTL = Roo.bootstrap.DateField.dates[this.language].rtl || false;
20869         this.format = this.format || 'm/d/y';
20870         this.isInline = false;
20871         this.isInput = true;
20872         this.component = this.el.select('.add-on', true).first() || false;
20873         this.component = (this.component && this.component.length === 0) ? false : this.component;
20874         this.hasInput = this.component && this.inputEl().length;
20875         
20876         if (typeof(this.minViewMode === 'string')) {
20877             switch (this.minViewMode) {
20878                 case 'months':
20879                     this.minViewMode = 1;
20880                     break;
20881                 case 'years':
20882                     this.minViewMode = 2;
20883                     break;
20884                 default:
20885                     this.minViewMode = 0;
20886                     break;
20887             }
20888         }
20889         
20890         if (typeof(this.viewMode === 'string')) {
20891             switch (this.viewMode) {
20892                 case 'months':
20893                     this.viewMode = 1;
20894                     break;
20895                 case 'years':
20896                     this.viewMode = 2;
20897                     break;
20898                 default:
20899                     this.viewMode = 0;
20900                     break;
20901             }
20902         }
20903                 
20904         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.DateField.template);
20905         
20906 //        this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.DateField.template);
20907         
20908         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
20909         
20910         this.picker().on('mousedown', this.onMousedown, this);
20911         this.picker().on('click', this.onClick, this);
20912         
20913         this.picker().addClass('datepicker-dropdown');
20914         
20915         this.startViewMode = this.viewMode;
20916         
20917         if(this.singleMode){
20918             Roo.each(this.picker().select('thead > tr > th', true).elements, function(v){
20919                 v.setVisibilityMode(Roo.Element.DISPLAY);
20920                 v.hide();
20921             });
20922             
20923             Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
20924                 v.setStyle('width', '189px');
20925             });
20926         }
20927         
20928         Roo.each(this.picker().select('tfoot th.today', true).elements, function(v){
20929             if(!this.calendarWeeks){
20930                 v.remove();
20931                 return;
20932             }
20933             
20934             v.dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
20935             v.attr('colspan', function(i, val){
20936                 return parseInt(val) + 1;
20937             });
20938         });
20939                         
20940         
20941         this.weekEnd = this.weekStart === 0 ? 6 : this.weekStart - 1;
20942         
20943         this.setStartDate(this.startDate);
20944         this.setEndDate(this.endDate);
20945         
20946         this.setDaysOfWeekDisabled(this.daysOfWeekDisabled);
20947         
20948         this.fillDow();
20949         this.fillMonths();
20950         this.update();
20951         this.showMode();
20952         
20953         if(this.isInline) {
20954             this.showPopup();
20955         }
20956     },
20957     
20958     picker : function()
20959     {
20960         return this.pickerEl;
20961 //        return this.el.select('.datepicker', true).first();
20962     },
20963     
20964     fillDow: function()
20965     {
20966         var dowCnt = this.weekStart;
20967         
20968         var dow = {
20969             tag: 'tr',
20970             cn: [
20971                 
20972             ]
20973         };
20974         
20975         if(this.calendarWeeks){
20976             dow.cn.push({
20977                 tag: 'th',
20978                 cls: 'cw',
20979                 html: '&nbsp;'
20980             })
20981         }
20982         
20983         while (dowCnt < this.weekStart + 7) {
20984             dow.cn.push({
20985                 tag: 'th',
20986                 cls: 'dow',
20987                 html: Roo.bootstrap.DateField.dates[this.language].daysMin[(dowCnt++)%7]
20988             });
20989         }
20990         
20991         this.picker().select('>.datepicker-days thead', true).first().createChild(dow);
20992     },
20993     
20994     fillMonths: function()
20995     {    
20996         var i = 0;
20997         var months = this.picker().select('>.datepicker-months td', true).first();
20998         
20999         months.dom.innerHTML = '';
21000         
21001         while (i < 12) {
21002             var month = {
21003                 tag: 'span',
21004                 cls: 'month',
21005                 html: Roo.bootstrap.DateField.dates[this.language].monthsShort[i++]
21006             };
21007             
21008             months.createChild(month);
21009         }
21010         
21011     },
21012     
21013     update: function()
21014     {
21015         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;
21016         
21017         if (this.date < this.startDate) {
21018             this.viewDate = new Date(this.startDate);
21019         } else if (this.date > this.endDate) {
21020             this.viewDate = new Date(this.endDate);
21021         } else {
21022             this.viewDate = new Date(this.date);
21023         }
21024         
21025         this.fill();
21026     },
21027     
21028     fill: function() 
21029     {
21030         var d = new Date(this.viewDate),
21031                 year = d.getUTCFullYear(),
21032                 month = d.getUTCMonth(),
21033                 startYear = this.startDate !== -Infinity ? this.startDate.getUTCFullYear() : -Infinity,
21034                 startMonth = this.startDate !== -Infinity ? this.startDate.getUTCMonth() : -Infinity,
21035                 endYear = this.endDate !== Infinity ? this.endDate.getUTCFullYear() : Infinity,
21036                 endMonth = this.endDate !== Infinity ? this.endDate.getUTCMonth() : Infinity,
21037                 currentDate = this.date && this.date.valueOf(),
21038                 today = this.UTCToday();
21039         
21040         this.picker().select('>.datepicker-days thead th.switch', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].months[month]+' '+year;
21041         
21042 //        this.picker().select('>tfoot th.today', true).first().dom.innerHTML = Roo.bootstrap.DateField.dates[this.language].today;
21043         
21044 //        this.picker.select('>tfoot th.today').
21045 //                                              .text(dates[this.language].today)
21046 //                                              .toggle(this.todayBtn !== false);
21047     
21048         this.updateNavArrows();
21049         this.fillMonths();
21050                                                 
21051         var prevMonth = this.UTCDate(year, month-1, 28,0,0,0,0),
21052         
21053         day = prevMonth.getDaysInMonth(prevMonth.getUTCFullYear(), prevMonth.getUTCMonth());
21054          
21055         prevMonth.setUTCDate(day);
21056         
21057         prevMonth.setUTCDate(day - (prevMonth.getUTCDay() - this.weekStart + 7)%7);
21058         
21059         var nextMonth = new Date(prevMonth);
21060         
21061         nextMonth.setUTCDate(nextMonth.getUTCDate() + 42);
21062         
21063         nextMonth = nextMonth.valueOf();
21064         
21065         var fillMonths = false;
21066         
21067         this.picker().select('>.datepicker-days tbody',true).first().dom.innerHTML = '';
21068         
21069         while(prevMonth.valueOf() <= nextMonth) {
21070             var clsName = '';
21071             
21072             if (prevMonth.getUTCDay() === this.weekStart) {
21073                 if(fillMonths){
21074                     this.picker().select('>.datepicker-days tbody',true).first().createChild(fillMonths);
21075                 }
21076                     
21077                 fillMonths = {
21078                     tag: 'tr',
21079                     cn: []
21080                 };
21081                 
21082                 if(this.calendarWeeks){
21083                     // ISO 8601: First week contains first thursday.
21084                     // ISO also states week starts on Monday, but we can be more abstract here.
21085                     var
21086                     // Start of current week: based on weekstart/current date
21087                     ws = new Date(+prevMonth + (this.weekStart - prevMonth.getUTCDay() - 7) % 7 * 864e5),
21088                     // Thursday of this week
21089                     th = new Date(+ws + (7 + 4 - ws.getUTCDay()) % 7 * 864e5),
21090                     // First Thursday of year, year from thursday
21091                     yth = new Date(+(yth = this.UTCDate(th.getUTCFullYear(), 0, 1)) + (7 + 4 - yth.getUTCDay())%7*864e5),
21092                     // Calendar week: ms between thursdays, div ms per day, div 7 days
21093                     calWeek =  (th - yth) / 864e5 / 7 + 1;
21094                     
21095                     fillMonths.cn.push({
21096                         tag: 'td',
21097                         cls: 'cw',
21098                         html: calWeek
21099                     });
21100                 }
21101             }
21102             
21103             if (prevMonth.getUTCFullYear() < year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() < month)) {
21104                 clsName += ' old';
21105             } else if (prevMonth.getUTCFullYear() > year || (prevMonth.getUTCFullYear() == year && prevMonth.getUTCMonth() > month)) {
21106                 clsName += ' new';
21107             }
21108             if (this.todayHighlight &&
21109                 prevMonth.getUTCFullYear() == today.getFullYear() &&
21110                 prevMonth.getUTCMonth() == today.getMonth() &&
21111                 prevMonth.getUTCDate() == today.getDate()) {
21112                 clsName += ' today';
21113             }
21114             
21115             if (currentDate && prevMonth.valueOf() === currentDate) {
21116                 clsName += ' active';
21117             }
21118             
21119             if (prevMonth.valueOf() < this.startDate || prevMonth.valueOf() > this.endDate ||
21120                     this.daysOfWeekDisabled.indexOf(prevMonth.getUTCDay()) !== -1) {
21121                     clsName += ' disabled';
21122             }
21123             
21124             fillMonths.cn.push({
21125                 tag: 'td',
21126                 cls: 'day ' + clsName,
21127                 html: prevMonth.getDate()
21128             });
21129             
21130             prevMonth.setDate(prevMonth.getDate()+1);
21131         }
21132           
21133         var currentYear = this.date && this.date.getUTCFullYear();
21134         var currentMonth = this.date && this.date.getUTCMonth();
21135         
21136         this.picker().select('>.datepicker-months th.switch',true).first().dom.innerHTML = year;
21137         
21138         Roo.each(this.picker().select('>.datepicker-months tbody span',true).elements, function(v,k){
21139             v.removeClass('active');
21140             
21141             if(currentYear === year && k === currentMonth){
21142                 v.addClass('active');
21143             }
21144             
21145             if (year < startYear || year > endYear || (year == startYear && k < startMonth) || (year == endYear && k > endMonth)) {
21146                 v.addClass('disabled');
21147             }
21148             
21149         });
21150         
21151         
21152         year = parseInt(year/10, 10) * 10;
21153         
21154         this.picker().select('>.datepicker-years th.switch', true).first().dom.innerHTML = year + '-' + (year + 9);
21155         
21156         this.picker().select('>.datepicker-years tbody td',true).first().dom.innerHTML = '';
21157         
21158         year -= 1;
21159         for (var i = -1; i < 11; i++) {
21160             this.picker().select('>.datepicker-years tbody td',true).first().createChild({
21161                 tag: 'span',
21162                 cls: 'year' + (i === -1 || i === 10 ? ' old' : '') + (currentYear === year ? ' active' : '') + (year < startYear || year > endYear ? ' disabled' : ''),
21163                 html: year
21164             });
21165             
21166             year += 1;
21167         }
21168     },
21169     
21170     showMode: function(dir) 
21171     {
21172         if (dir) {
21173             this.viewMode = Math.max(this.minViewMode, Math.min(2, this.viewMode + dir));
21174         }
21175         
21176         Roo.each(this.picker().select('>div',true).elements, function(v){
21177             v.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21178             v.hide();
21179         });
21180         this.picker().select('>.datepicker-'+Roo.bootstrap.DateField.modes[this.viewMode].clsName, true).first().show();
21181     },
21182     
21183     place: function()
21184     {
21185         if(this.isInline) {
21186             return;
21187         }
21188         
21189         this.picker().removeClass(['bottom', 'top']);
21190         
21191         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
21192             /*
21193              * place to the top of element!
21194              *
21195              */
21196             
21197             this.picker().addClass('top');
21198             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
21199             
21200             return;
21201         }
21202         
21203         this.picker().addClass('bottom');
21204         
21205         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
21206     },
21207     
21208     parseDate : function(value)
21209     {
21210         if(!value || value instanceof Date){
21211             return value;
21212         }
21213         var v = Date.parseDate(value, this.format);
21214         if (!v && (this.useIso || value.match(/^(\d{4})-0?(\d+)-0?(\d+)/))) {
21215             v = Date.parseDate(value, 'Y-m-d');
21216         }
21217         if(!v && this.altFormats){
21218             if(!this.altFormatsArray){
21219                 this.altFormatsArray = this.altFormats.split("|");
21220             }
21221             for(var i = 0, len = this.altFormatsArray.length; i < len && !v; i++){
21222                 v = Date.parseDate(value, this.altFormatsArray[i]);
21223             }
21224         }
21225         return v;
21226     },
21227     
21228     formatDate : function(date, fmt)
21229     {   
21230         return (!date || !(date instanceof Date)) ?
21231         date : date.dateFormat(fmt || this.format);
21232     },
21233     
21234     onFocus : function()
21235     {
21236         Roo.bootstrap.DateField.superclass.onFocus.call(this);
21237         this.showPopup();
21238     },
21239     
21240     onBlur : function()
21241     {
21242         Roo.bootstrap.DateField.superclass.onBlur.call(this);
21243         
21244         var d = this.inputEl().getValue();
21245         
21246         this.setValue(d);
21247                 
21248         this.hidePopup();
21249     },
21250     
21251     showPopup : function()
21252     {
21253         this.picker().show();
21254         this.update();
21255         this.place();
21256         
21257         this.fireEvent('showpopup', this, this.date);
21258     },
21259     
21260     hidePopup : function()
21261     {
21262         if(this.isInline) {
21263             return;
21264         }
21265         this.picker().hide();
21266         this.viewMode = this.startViewMode;
21267         this.showMode();
21268         
21269         this.fireEvent('hidepopup', this, this.date);
21270         
21271     },
21272     
21273     onMousedown: function(e)
21274     {
21275         e.stopPropagation();
21276         e.preventDefault();
21277     },
21278     
21279     keyup: function(e)
21280     {
21281         Roo.bootstrap.DateField.superclass.keyup.call(this);
21282         this.update();
21283     },
21284
21285     setValue: function(v)
21286     {
21287         if(this.fireEvent('beforeselect', this, v) !== false){
21288             var d = new Date(this.parseDate(v) ).clearTime();
21289         
21290             if(isNaN(d.getTime())){
21291                 this.date = this.viewDate = '';
21292                 Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21293                 return;
21294             }
21295
21296             v = this.formatDate(d);
21297
21298             Roo.bootstrap.DateField.superclass.setValue.call(this, v);
21299
21300             this.date = new Date(d.getTime() - d.getTimezoneOffset()*60000);
21301
21302             this.update();
21303
21304             this.fireEvent('select', this, this.date);
21305         }
21306     },
21307     
21308     getValue: function()
21309     {
21310         return this.formatDate(this.date);
21311     },
21312     
21313     fireKey: function(e)
21314     {
21315         if (!this.picker().isVisible()){
21316             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21317                 this.showPopup();
21318             }
21319             return;
21320         }
21321         
21322         var dateChanged = false,
21323         dir, day, month,
21324         newDate, newViewDate;
21325         
21326         switch(e.keyCode){
21327             case 27: // escape
21328                 this.hidePopup();
21329                 e.preventDefault();
21330                 break;
21331             case 37: // left
21332             case 39: // right
21333                 if (!this.keyboardNavigation) {
21334                     break;
21335                 }
21336                 dir = e.keyCode == 37 ? -1 : 1;
21337                 
21338                 if (e.ctrlKey){
21339                     newDate = this.moveYear(this.date, dir);
21340                     newViewDate = this.moveYear(this.viewDate, dir);
21341                 } else if (e.shiftKey){
21342                     newDate = this.moveMonth(this.date, dir);
21343                     newViewDate = this.moveMonth(this.viewDate, dir);
21344                 } else {
21345                     newDate = new Date(this.date);
21346                     newDate.setUTCDate(this.date.getUTCDate() + dir);
21347                     newViewDate = new Date(this.viewDate);
21348                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir);
21349                 }
21350                 if (this.dateWithinRange(newDate)){
21351                     this.date = newDate;
21352                     this.viewDate = newViewDate;
21353                     this.setValue(this.formatDate(this.date));
21354 //                    this.update();
21355                     e.preventDefault();
21356                     dateChanged = true;
21357                 }
21358                 break;
21359             case 38: // up
21360             case 40: // down
21361                 if (!this.keyboardNavigation) {
21362                     break;
21363                 }
21364                 dir = e.keyCode == 38 ? -1 : 1;
21365                 if (e.ctrlKey){
21366                     newDate = this.moveYear(this.date, dir);
21367                     newViewDate = this.moveYear(this.viewDate, dir);
21368                 } else if (e.shiftKey){
21369                     newDate = this.moveMonth(this.date, dir);
21370                     newViewDate = this.moveMonth(this.viewDate, dir);
21371                 } else {
21372                     newDate = new Date(this.date);
21373                     newDate.setUTCDate(this.date.getUTCDate() + dir * 7);
21374                     newViewDate = new Date(this.viewDate);
21375                     newViewDate.setUTCDate(this.viewDate.getUTCDate() + dir * 7);
21376                 }
21377                 if (this.dateWithinRange(newDate)){
21378                     this.date = newDate;
21379                     this.viewDate = newViewDate;
21380                     this.setValue(this.formatDate(this.date));
21381 //                    this.update();
21382                     e.preventDefault();
21383                     dateChanged = true;
21384                 }
21385                 break;
21386             case 13: // enter
21387                 this.setValue(this.formatDate(this.date));
21388                 this.hidePopup();
21389                 e.preventDefault();
21390                 break;
21391             case 9: // tab
21392                 this.setValue(this.formatDate(this.date));
21393                 this.hidePopup();
21394                 break;
21395             case 16: // shift
21396             case 17: // ctrl
21397             case 18: // alt
21398                 break;
21399             default :
21400                 this.hidePopup();
21401                 
21402         }
21403     },
21404     
21405     
21406     onClick: function(e) 
21407     {
21408         e.stopPropagation();
21409         e.preventDefault();
21410         
21411         var target = e.getTarget();
21412         
21413         if(target.nodeName.toLowerCase() === 'i'){
21414             target = Roo.get(target).dom.parentNode;
21415         }
21416         
21417         var nodeName = target.nodeName;
21418         var className = target.className;
21419         var html = target.innerHTML;
21420         //Roo.log(nodeName);
21421         
21422         switch(nodeName.toLowerCase()) {
21423             case 'th':
21424                 switch(className) {
21425                     case 'switch':
21426                         this.showMode(1);
21427                         break;
21428                     case 'prev':
21429                     case 'next':
21430                         var dir = Roo.bootstrap.DateField.modes[this.viewMode].navStep * (className == 'prev' ? -1 : 1);
21431                         switch(this.viewMode){
21432                                 case 0:
21433                                         this.viewDate = this.moveMonth(this.viewDate, dir);
21434                                         break;
21435                                 case 1:
21436                                 case 2:
21437                                         this.viewDate = this.moveYear(this.viewDate, dir);
21438                                         break;
21439                         }
21440                         this.fill();
21441                         break;
21442                     case 'today':
21443                         var date = new Date();
21444                         this.date = this.UTCDate(date.getFullYear(), date.getMonth(), date.getDate(), 0, 0, 0);
21445 //                        this.fill()
21446                         this.setValue(this.formatDate(this.date));
21447                         
21448                         this.hidePopup();
21449                         break;
21450                 }
21451                 break;
21452             case 'span':
21453                 if (className.indexOf('disabled') < 0) {
21454                     this.viewDate.setUTCDate(1);
21455                     if (className.indexOf('month') > -1) {
21456                         this.viewDate.setUTCMonth(Roo.bootstrap.DateField.dates[this.language].monthsShort.indexOf(html));
21457                     } else {
21458                         var year = parseInt(html, 10) || 0;
21459                         this.viewDate.setUTCFullYear(year);
21460                         
21461                     }
21462                     
21463                     if(this.singleMode){
21464                         this.setValue(this.formatDate(this.viewDate));
21465                         this.hidePopup();
21466                         return;
21467                     }
21468                     
21469                     this.showMode(-1);
21470                     this.fill();
21471                 }
21472                 break;
21473                 
21474             case 'td':
21475                 //Roo.log(className);
21476                 if (className.indexOf('day') > -1 && className.indexOf('disabled') < 0 ){
21477                     var day = parseInt(html, 10) || 1;
21478                     var year = this.viewDate.getUTCFullYear(),
21479                         month = this.viewDate.getUTCMonth();
21480
21481                     if (className.indexOf('old') > -1) {
21482                         if(month === 0 ){
21483                             month = 11;
21484                             year -= 1;
21485                         }else{
21486                             month -= 1;
21487                         }
21488                     } else if (className.indexOf('new') > -1) {
21489                         if (month == 11) {
21490                             month = 0;
21491                             year += 1;
21492                         } else {
21493                             month += 1;
21494                         }
21495                     }
21496                     //Roo.log([year,month,day]);
21497                     this.date = this.UTCDate(year, month, day,0,0,0,0);
21498                     this.viewDate = this.UTCDate(year, month, Math.min(28, day),0,0,0,0);
21499 //                    this.fill();
21500                     //Roo.log(this.formatDate(this.date));
21501                     this.setValue(this.formatDate(this.date));
21502                     this.hidePopup();
21503                 }
21504                 break;
21505         }
21506     },
21507     
21508     setStartDate: function(startDate)
21509     {
21510         this.startDate = startDate || -Infinity;
21511         if (this.startDate !== -Infinity) {
21512             this.startDate = this.parseDate(this.startDate);
21513         }
21514         this.update();
21515         this.updateNavArrows();
21516     },
21517
21518     setEndDate: function(endDate)
21519     {
21520         this.endDate = endDate || Infinity;
21521         if (this.endDate !== Infinity) {
21522             this.endDate = this.parseDate(this.endDate);
21523         }
21524         this.update();
21525         this.updateNavArrows();
21526     },
21527     
21528     setDaysOfWeekDisabled: function(daysOfWeekDisabled)
21529     {
21530         this.daysOfWeekDisabled = daysOfWeekDisabled || [];
21531         if (typeof(this.daysOfWeekDisabled) !== 'object') {
21532             this.daysOfWeekDisabled = this.daysOfWeekDisabled.split(/,\s*/);
21533         }
21534         this.daysOfWeekDisabled = this.daysOfWeekDisabled.map(function (d) {
21535             return parseInt(d, 10);
21536         });
21537         this.update();
21538         this.updateNavArrows();
21539     },
21540     
21541     updateNavArrows: function() 
21542     {
21543         if(this.singleMode){
21544             return;
21545         }
21546         
21547         var d = new Date(this.viewDate),
21548         year = d.getUTCFullYear(),
21549         month = d.getUTCMonth();
21550         
21551         Roo.each(this.picker().select('.prev', true).elements, function(v){
21552             v.show();
21553             switch (this.viewMode) {
21554                 case 0:
21555
21556                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear() && month <= this.startDate.getUTCMonth()) {
21557                         v.hide();
21558                     }
21559                     break;
21560                 case 1:
21561                 case 2:
21562                     if (this.startDate !== -Infinity && year <= this.startDate.getUTCFullYear()) {
21563                         v.hide();
21564                     }
21565                     break;
21566             }
21567         });
21568         
21569         Roo.each(this.picker().select('.next', true).elements, function(v){
21570             v.show();
21571             switch (this.viewMode) {
21572                 case 0:
21573
21574                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear() && month >= this.endDate.getUTCMonth()) {
21575                         v.hide();
21576                     }
21577                     break;
21578                 case 1:
21579                 case 2:
21580                     if (this.endDate !== Infinity && year >= this.endDate.getUTCFullYear()) {
21581                         v.hide();
21582                     }
21583                     break;
21584             }
21585         })
21586     },
21587     
21588     moveMonth: function(date, dir)
21589     {
21590         if (!dir) {
21591             return date;
21592         }
21593         var new_date = new Date(date.valueOf()),
21594         day = new_date.getUTCDate(),
21595         month = new_date.getUTCMonth(),
21596         mag = Math.abs(dir),
21597         new_month, test;
21598         dir = dir > 0 ? 1 : -1;
21599         if (mag == 1){
21600             test = dir == -1
21601             // If going back one month, make sure month is not current month
21602             // (eg, Mar 31 -> Feb 31 == Feb 28, not Mar 02)
21603             ? function(){
21604                 return new_date.getUTCMonth() == month;
21605             }
21606             // If going forward one month, make sure month is as expected
21607             // (eg, Jan 31 -> Feb 31 == Feb 28, not Mar 02)
21608             : function(){
21609                 return new_date.getUTCMonth() != new_month;
21610             };
21611             new_month = month + dir;
21612             new_date.setUTCMonth(new_month);
21613             // Dec -> Jan (12) or Jan -> Dec (-1) -- limit expected date to 0-11
21614             if (new_month < 0 || new_month > 11) {
21615                 new_month = (new_month + 12) % 12;
21616             }
21617         } else {
21618             // For magnitudes >1, move one month at a time...
21619             for (var i=0; i<mag; i++) {
21620                 // ...which might decrease the day (eg, Jan 31 to Feb 28, etc)...
21621                 new_date = this.moveMonth(new_date, dir);
21622             }
21623             // ...then reset the day, keeping it in the new month
21624             new_month = new_date.getUTCMonth();
21625             new_date.setUTCDate(day);
21626             test = function(){
21627                 return new_month != new_date.getUTCMonth();
21628             };
21629         }
21630         // Common date-resetting loop -- if date is beyond end of month, make it
21631         // end of month
21632         while (test()){
21633             new_date.setUTCDate(--day);
21634             new_date.setUTCMonth(new_month);
21635         }
21636         return new_date;
21637     },
21638
21639     moveYear: function(date, dir)
21640     {
21641         return this.moveMonth(date, dir*12);
21642     },
21643
21644     dateWithinRange: function(date)
21645     {
21646         return date >= this.startDate && date <= this.endDate;
21647     },
21648
21649     
21650     remove: function() 
21651     {
21652         this.picker().remove();
21653     },
21654     
21655     validateValue : function(value)
21656     {
21657         if(this.getVisibilityEl().hasClass('hidden')){
21658             return true;
21659         }
21660         
21661         if(value.length < 1)  {
21662             if(this.allowBlank){
21663                 return true;
21664             }
21665             return false;
21666         }
21667         
21668         if(value.length < this.minLength){
21669             return false;
21670         }
21671         if(value.length > this.maxLength){
21672             return false;
21673         }
21674         if(this.vtype){
21675             var vt = Roo.form.VTypes;
21676             if(!vt[this.vtype](value, this)){
21677                 return false;
21678             }
21679         }
21680         if(typeof this.validator == "function"){
21681             var msg = this.validator(value);
21682             if(msg !== true){
21683                 return false;
21684             }
21685         }
21686         
21687         if(this.regex && !this.regex.test(value)){
21688             return false;
21689         }
21690         
21691         if(typeof(this.parseDate(value)) == 'undefined'){
21692             return false;
21693         }
21694         
21695         if (this.endDate !== Infinity && this.parseDate(value).getTime() > this.endDate.getTime()) {
21696             return false;
21697         }      
21698         
21699         if (this.startDate !== -Infinity && this.parseDate(value).getTime() < this.startDate.getTime()) {
21700             return false;
21701         } 
21702         
21703         
21704         return true;
21705     },
21706     
21707     reset : function()
21708     {
21709         this.date = this.viewDate = '';
21710         
21711         Roo.bootstrap.DateField.superclass.setValue.call(this, '');
21712     }
21713    
21714 });
21715
21716 Roo.apply(Roo.bootstrap.DateField,  {
21717     
21718     head : {
21719         tag: 'thead',
21720         cn: [
21721         {
21722             tag: 'tr',
21723             cn: [
21724             {
21725                 tag: 'th',
21726                 cls: 'prev',
21727                 html: '<i class="fa fa-arrow-left"/>'
21728             },
21729             {
21730                 tag: 'th',
21731                 cls: 'switch',
21732                 colspan: '5'
21733             },
21734             {
21735                 tag: 'th',
21736                 cls: 'next',
21737                 html: '<i class="fa fa-arrow-right"/>'
21738             }
21739
21740             ]
21741         }
21742         ]
21743     },
21744     
21745     content : {
21746         tag: 'tbody',
21747         cn: [
21748         {
21749             tag: 'tr',
21750             cn: [
21751             {
21752                 tag: 'td',
21753                 colspan: '7'
21754             }
21755             ]
21756         }
21757         ]
21758     },
21759     
21760     footer : {
21761         tag: 'tfoot',
21762         cn: [
21763         {
21764             tag: 'tr',
21765             cn: [
21766             {
21767                 tag: 'th',
21768                 colspan: '7',
21769                 cls: 'today'
21770             }
21771                     
21772             ]
21773         }
21774         ]
21775     },
21776     
21777     dates:{
21778         en: {
21779             days: ["Sunday", "Monday", "Tuesday", "Wednesday", "Thursday", "Friday", "Saturday", "Sunday"],
21780             daysShort: ["Sun", "Mon", "Tue", "Wed", "Thu", "Fri", "Sat", "Sun"],
21781             daysMin: ["Su", "Mo", "Tu", "We", "Th", "Fr", "Sa", "Su"],
21782             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
21783             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"],
21784             today: "Today"
21785         }
21786     },
21787     
21788     modes: [
21789     {
21790         clsName: 'days',
21791         navFnc: 'Month',
21792         navStep: 1
21793     },
21794     {
21795         clsName: 'months',
21796         navFnc: 'FullYear',
21797         navStep: 1
21798     },
21799     {
21800         clsName: 'years',
21801         navFnc: 'FullYear',
21802         navStep: 10
21803     }]
21804 });
21805
21806 Roo.apply(Roo.bootstrap.DateField,  {
21807   
21808     template : {
21809         tag: 'div',
21810         cls: 'datepicker dropdown-menu roo-dynamic shadow',
21811         cn: [
21812         {
21813             tag: 'div',
21814             cls: 'datepicker-days',
21815             cn: [
21816             {
21817                 tag: 'table',
21818                 cls: 'table-condensed',
21819                 cn:[
21820                 Roo.bootstrap.DateField.head,
21821                 {
21822                     tag: 'tbody'
21823                 },
21824                 Roo.bootstrap.DateField.footer
21825                 ]
21826             }
21827             ]
21828         },
21829         {
21830             tag: 'div',
21831             cls: 'datepicker-months',
21832             cn: [
21833             {
21834                 tag: 'table',
21835                 cls: 'table-condensed',
21836                 cn:[
21837                 Roo.bootstrap.DateField.head,
21838                 Roo.bootstrap.DateField.content,
21839                 Roo.bootstrap.DateField.footer
21840                 ]
21841             }
21842             ]
21843         },
21844         {
21845             tag: 'div',
21846             cls: 'datepicker-years',
21847             cn: [
21848             {
21849                 tag: 'table',
21850                 cls: 'table-condensed',
21851                 cn:[
21852                 Roo.bootstrap.DateField.head,
21853                 Roo.bootstrap.DateField.content,
21854                 Roo.bootstrap.DateField.footer
21855                 ]
21856             }
21857             ]
21858         }
21859         ]
21860     }
21861 });
21862
21863  
21864
21865  /*
21866  * - LGPL
21867  *
21868  * TimeField
21869  * 
21870  */
21871
21872 /**
21873  * @class Roo.bootstrap.TimeField
21874  * @extends Roo.bootstrap.Input
21875  * Bootstrap DateField class
21876  * 
21877  * 
21878  * @constructor
21879  * Create a new TimeField
21880  * @param {Object} config The config object
21881  */
21882
21883 Roo.bootstrap.TimeField = function(config){
21884     Roo.bootstrap.TimeField.superclass.constructor.call(this, config);
21885     this.addEvents({
21886             /**
21887              * @event show
21888              * Fires when this field show.
21889              * @param {Roo.bootstrap.DateField} thisthis
21890              * @param {Mixed} date The date value
21891              */
21892             show : true,
21893             /**
21894              * @event show
21895              * Fires when this field hide.
21896              * @param {Roo.bootstrap.DateField} this
21897              * @param {Mixed} date The date value
21898              */
21899             hide : true,
21900             /**
21901              * @event select
21902              * Fires when select a date.
21903              * @param {Roo.bootstrap.DateField} this
21904              * @param {Mixed} date The date value
21905              */
21906             select : true
21907         });
21908 };
21909
21910 Roo.extend(Roo.bootstrap.TimeField, Roo.bootstrap.Input,  {
21911     
21912     /**
21913      * @cfg {String} format
21914      * The default time format string which can be overriden for localization support.  The format must be
21915      * valid according to {@link Date#parseDate} (defaults to 'H:i').
21916      */
21917     format : "H:i",
21918        
21919     onRender: function(ct, position)
21920     {
21921         
21922         Roo.bootstrap.TimeField.superclass.onRender.call(this, ct, position);
21923                 
21924         this.el.select('>.input-group', true).first().createChild(Roo.bootstrap.TimeField.template);
21925         
21926         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21927         
21928         this.pop = this.picker().select('>.datepicker-time',true).first();
21929         this.pop.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
21930         
21931         this.picker().on('mousedown', this.onMousedown, this);
21932         this.picker().on('click', this.onClick, this);
21933         
21934         this.picker().addClass('datepicker-dropdown');
21935     
21936         this.fillTime();
21937         this.update();
21938             
21939         this.pop.select('span.hours-up', true).first().on('click', this.onIncrementHours, this);
21940         this.pop.select('span.hours-down', true).first().on('click', this.onDecrementHours, this);
21941         this.pop.select('span.minutes-up', true).first().on('click', this.onIncrementMinutes, this);
21942         this.pop.select('span.minutes-down', true).first().on('click', this.onDecrementMinutes, this);
21943         this.pop.select('button.period', true).first().on('click', this.onTogglePeriod, this);
21944         this.pop.select('button.ok', true).first().on('click', this.setTime, this);
21945
21946     },
21947     
21948     fireKey: function(e){
21949         if (!this.picker().isVisible()){
21950             if (e.keyCode == 27) { // allow escape to hide and re-show picker
21951                 this.show();
21952             }
21953             return;
21954         }
21955
21956         e.preventDefault();
21957         
21958         switch(e.keyCode){
21959             case 27: // escape
21960                 this.hide();
21961                 break;
21962             case 37: // left
21963             case 39: // right
21964                 this.onTogglePeriod();
21965                 break;
21966             case 38: // up
21967                 this.onIncrementMinutes();
21968                 break;
21969             case 40: // down
21970                 this.onDecrementMinutes();
21971                 break;
21972             case 13: // enter
21973             case 9: // tab
21974                 this.setTime();
21975                 break;
21976         }
21977     },
21978     
21979     onClick: function(e) {
21980         e.stopPropagation();
21981         e.preventDefault();
21982     },
21983     
21984     picker : function()
21985     {
21986         return this.el.select('.datepicker', true).first();
21987     },
21988     
21989     fillTime: function()
21990     {    
21991         var time = this.pop.select('tbody', true).first();
21992         
21993         time.dom.innerHTML = '';
21994         
21995         time.createChild({
21996             tag: 'tr',
21997             cn: [
21998                 {
21999                     tag: 'td',
22000                     cn: [
22001                         {
22002                             tag: 'a',
22003                             href: '#',
22004                             cls: 'btn',
22005                             cn: [
22006                                 {
22007                                     tag: 'span',
22008                                     cls: 'hours-up glyphicon glyphicon-chevron-up'
22009                                 }
22010                             ]
22011                         } 
22012                     ]
22013                 },
22014                 {
22015                     tag: 'td',
22016                     cls: 'separator'
22017                 },
22018                 {
22019                     tag: 'td',
22020                     cn: [
22021                         {
22022                             tag: 'a',
22023                             href: '#',
22024                             cls: 'btn',
22025                             cn: [
22026                                 {
22027                                     tag: 'span',
22028                                     cls: 'minutes-up glyphicon glyphicon-chevron-up'
22029                                 }
22030                             ]
22031                         }
22032                     ]
22033                 },
22034                 {
22035                     tag: 'td',
22036                     cls: 'separator'
22037                 }
22038             ]
22039         });
22040         
22041         time.createChild({
22042             tag: 'tr',
22043             cn: [
22044                 {
22045                     tag: 'td',
22046                     cn: [
22047                         {
22048                             tag: 'span',
22049                             cls: 'timepicker-hour',
22050                             html: '00'
22051                         }  
22052                     ]
22053                 },
22054                 {
22055                     tag: 'td',
22056                     cls: 'separator',
22057                     html: ':'
22058                 },
22059                 {
22060                     tag: 'td',
22061                     cn: [
22062                         {
22063                             tag: 'span',
22064                             cls: 'timepicker-minute',
22065                             html: '00'
22066                         }  
22067                     ]
22068                 },
22069                 {
22070                     tag: 'td',
22071                     cls: 'separator'
22072                 },
22073                 {
22074                     tag: 'td',
22075                     cn: [
22076                         {
22077                             tag: 'button',
22078                             type: 'button',
22079                             cls: 'btn btn-primary period',
22080                             html: 'AM'
22081                             
22082                         }
22083                     ]
22084                 }
22085             ]
22086         });
22087         
22088         time.createChild({
22089             tag: 'tr',
22090             cn: [
22091                 {
22092                     tag: 'td',
22093                     cn: [
22094                         {
22095                             tag: 'a',
22096                             href: '#',
22097                             cls: 'btn',
22098                             cn: [
22099                                 {
22100                                     tag: 'span',
22101                                     cls: 'hours-down glyphicon glyphicon-chevron-down'
22102                                 }
22103                             ]
22104                         }
22105                     ]
22106                 },
22107                 {
22108                     tag: 'td',
22109                     cls: 'separator'
22110                 },
22111                 {
22112                     tag: 'td',
22113                     cn: [
22114                         {
22115                             tag: 'a',
22116                             href: '#',
22117                             cls: 'btn',
22118                             cn: [
22119                                 {
22120                                     tag: 'span',
22121                                     cls: 'minutes-down glyphicon glyphicon-chevron-down'
22122                                 }
22123                             ]
22124                         }
22125                     ]
22126                 },
22127                 {
22128                     tag: 'td',
22129                     cls: 'separator'
22130                 }
22131             ]
22132         });
22133         
22134     },
22135     
22136     update: function()
22137     {
22138         
22139         this.time = (typeof(this.time) === 'undefined') ? new Date() : this.time;
22140         
22141         this.fill();
22142     },
22143     
22144     fill: function() 
22145     {
22146         var hours = this.time.getHours();
22147         var minutes = this.time.getMinutes();
22148         var period = 'AM';
22149         
22150         if(hours > 11){
22151             period = 'PM';
22152         }
22153         
22154         if(hours == 0){
22155             hours = 12;
22156         }
22157         
22158         
22159         if(hours > 12){
22160             hours = hours - 12;
22161         }
22162         
22163         if(hours < 10){
22164             hours = '0' + hours;
22165         }
22166         
22167         if(minutes < 10){
22168             minutes = '0' + minutes;
22169         }
22170         
22171         this.pop.select('.timepicker-hour', true).first().dom.innerHTML = hours;
22172         this.pop.select('.timepicker-minute', true).first().dom.innerHTML = minutes;
22173         this.pop.select('button', true).first().dom.innerHTML = period;
22174         
22175     },
22176     
22177     place: function()
22178     {   
22179         this.picker().removeClass(['bottom-left', 'bottom-right', 'top-left', 'top-right']);
22180         
22181         var cls = ['bottom'];
22182         
22183         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){ // top
22184             cls.pop();
22185             cls.push('top');
22186         }
22187         
22188         cls.push('right');
22189         
22190         if((Roo.lib.Dom.getViewWidth() + Roo.get(document.body).getScroll().left) - (this.inputEl().getLeft() + this.picker().getWidth()) < 0){ // left
22191             cls.pop();
22192             cls.push('left');
22193         }
22194         
22195         this.picker().addClass(cls.join('-'));
22196         
22197         var _this = this;
22198         
22199         Roo.each(cls, function(c){
22200             if(c == 'bottom'){
22201                 _this.picker().setTop(_this.inputEl().getHeight());
22202                 return;
22203             }
22204             if(c == 'top'){
22205                 _this.picker().setTop(0 - _this.picker().getHeight());
22206                 return;
22207             }
22208             
22209             if(c == 'left'){
22210                 _this.picker().setLeft(_this.inputEl().getLeft() + _this.inputEl().getWidth() - _this.el.getLeft() - _this.picker().getWidth());
22211                 return;
22212             }
22213             if(c == 'right'){
22214                 _this.picker().setLeft(_this.inputEl().getLeft() - _this.el.getLeft());
22215                 return;
22216             }
22217         });
22218         
22219     },
22220   
22221     onFocus : function()
22222     {
22223         Roo.bootstrap.TimeField.superclass.onFocus.call(this);
22224         this.show();
22225     },
22226     
22227     onBlur : function()
22228     {
22229         Roo.bootstrap.TimeField.superclass.onBlur.call(this);
22230         this.hide();
22231     },
22232     
22233     show : function()
22234     {
22235         this.picker().show();
22236         this.pop.show();
22237         this.update();
22238         this.place();
22239         
22240         this.fireEvent('show', this, this.date);
22241     },
22242     
22243     hide : function()
22244     {
22245         this.picker().hide();
22246         this.pop.hide();
22247         
22248         this.fireEvent('hide', this, this.date);
22249     },
22250     
22251     setTime : function()
22252     {
22253         this.hide();
22254         this.setValue(this.time.format(this.format));
22255         
22256         this.fireEvent('select', this, this.date);
22257         
22258         
22259     },
22260     
22261     onMousedown: function(e){
22262         e.stopPropagation();
22263         e.preventDefault();
22264     },
22265     
22266     onIncrementHours: function()
22267     {
22268         Roo.log('onIncrementHours');
22269         this.time = this.time.add(Date.HOUR, 1);
22270         this.update();
22271         
22272     },
22273     
22274     onDecrementHours: function()
22275     {
22276         Roo.log('onDecrementHours');
22277         this.time = this.time.add(Date.HOUR, -1);
22278         this.update();
22279     },
22280     
22281     onIncrementMinutes: function()
22282     {
22283         Roo.log('onIncrementMinutes');
22284         this.time = this.time.add(Date.MINUTE, 1);
22285         this.update();
22286     },
22287     
22288     onDecrementMinutes: function()
22289     {
22290         Roo.log('onDecrementMinutes');
22291         this.time = this.time.add(Date.MINUTE, -1);
22292         this.update();
22293     },
22294     
22295     onTogglePeriod: function()
22296     {
22297         Roo.log('onTogglePeriod');
22298         this.time = this.time.add(Date.HOUR, 12);
22299         this.update();
22300     }
22301     
22302    
22303 });
22304
22305 Roo.apply(Roo.bootstrap.TimeField,  {
22306     
22307     content : {
22308         tag: 'tbody',
22309         cn: [
22310             {
22311                 tag: 'tr',
22312                 cn: [
22313                 {
22314                     tag: 'td',
22315                     colspan: '7'
22316                 }
22317                 ]
22318             }
22319         ]
22320     },
22321     
22322     footer : {
22323         tag: 'tfoot',
22324         cn: [
22325             {
22326                 tag: 'tr',
22327                 cn: [
22328                 {
22329                     tag: 'th',
22330                     colspan: '7',
22331                     cls: '',
22332                     cn: [
22333                         {
22334                             tag: 'button',
22335                             cls: 'btn btn-info ok',
22336                             html: 'OK'
22337                         }
22338                     ]
22339                 }
22340
22341                 ]
22342             }
22343         ]
22344     }
22345 });
22346
22347 Roo.apply(Roo.bootstrap.TimeField,  {
22348   
22349     template : {
22350         tag: 'div',
22351         cls: 'datepicker dropdown-menu',
22352         cn: [
22353             {
22354                 tag: 'div',
22355                 cls: 'datepicker-time',
22356                 cn: [
22357                 {
22358                     tag: 'table',
22359                     cls: 'table-condensed',
22360                     cn:[
22361                     Roo.bootstrap.TimeField.content,
22362                     Roo.bootstrap.TimeField.footer
22363                     ]
22364                 }
22365                 ]
22366             }
22367         ]
22368     }
22369 });
22370
22371  
22372
22373  /*
22374  * - LGPL
22375  *
22376  * MonthField
22377  * 
22378  */
22379
22380 /**
22381  * @class Roo.bootstrap.MonthField
22382  * @extends Roo.bootstrap.Input
22383  * Bootstrap MonthField class
22384  * 
22385  * @cfg {String} language default en
22386  * 
22387  * @constructor
22388  * Create a new MonthField
22389  * @param {Object} config The config object
22390  */
22391
22392 Roo.bootstrap.MonthField = function(config){
22393     Roo.bootstrap.MonthField.superclass.constructor.call(this, config);
22394     
22395     this.addEvents({
22396         /**
22397          * @event show
22398          * Fires when this field show.
22399          * @param {Roo.bootstrap.MonthField} this
22400          * @param {Mixed} date The date value
22401          */
22402         show : true,
22403         /**
22404          * @event show
22405          * Fires when this field hide.
22406          * @param {Roo.bootstrap.MonthField} this
22407          * @param {Mixed} date The date value
22408          */
22409         hide : true,
22410         /**
22411          * @event select
22412          * Fires when select a date.
22413          * @param {Roo.bootstrap.MonthField} this
22414          * @param {String} oldvalue The old value
22415          * @param {String} newvalue The new value
22416          */
22417         select : true
22418     });
22419 };
22420
22421 Roo.extend(Roo.bootstrap.MonthField, Roo.bootstrap.Input,  {
22422     
22423     onRender: function(ct, position)
22424     {
22425         
22426         Roo.bootstrap.MonthField.superclass.onRender.call(this, ct, position);
22427         
22428         this.language = this.language || 'en';
22429         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : this.language.split('-')[0];
22430         this.language = this.language in Roo.bootstrap.MonthField.dates ? this.language : "en";
22431         
22432         this.isRTL = Roo.bootstrap.MonthField.dates[this.language].rtl || false;
22433         this.isInline = false;
22434         this.isInput = true;
22435         this.component = this.el.select('.add-on', true).first() || false;
22436         this.component = (this.component && this.component.length === 0) ? false : this.component;
22437         this.hasInput = this.component && this.inputEL().length;
22438         
22439         this.pickerEl = Roo.get(document.body).createChild(Roo.bootstrap.MonthField.template);
22440         
22441         this.picker().setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
22442         
22443         this.picker().on('mousedown', this.onMousedown, this);
22444         this.picker().on('click', this.onClick, this);
22445         
22446         this.picker().addClass('datepicker-dropdown');
22447         
22448         Roo.each(this.picker().select('tbody > tr > td', true).elements, function(v){
22449             v.setStyle('width', '189px');
22450         });
22451         
22452         this.fillMonths();
22453         
22454         this.update();
22455         
22456         if(this.isInline) {
22457             this.show();
22458         }
22459         
22460     },
22461     
22462     setValue: function(v, suppressEvent)
22463     {   
22464         var o = this.getValue();
22465         
22466         Roo.bootstrap.MonthField.superclass.setValue.call(this, v);
22467         
22468         this.update();
22469
22470         if(suppressEvent !== true){
22471             this.fireEvent('select', this, o, v);
22472         }
22473         
22474     },
22475     
22476     getValue: function()
22477     {
22478         return this.value;
22479     },
22480     
22481     onClick: function(e) 
22482     {
22483         e.stopPropagation();
22484         e.preventDefault();
22485         
22486         var target = e.getTarget();
22487         
22488         if(target.nodeName.toLowerCase() === 'i'){
22489             target = Roo.get(target).dom.parentNode;
22490         }
22491         
22492         var nodeName = target.nodeName;
22493         var className = target.className;
22494         var html = target.innerHTML;
22495         
22496         if(nodeName.toLowerCase() != 'span' || className.indexOf('disabled') > -1 || className.indexOf('month') == -1){
22497             return;
22498         }
22499         
22500         this.vIndex = Roo.bootstrap.MonthField.dates[this.language].monthsShort.indexOf(html);
22501         
22502         this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22503         
22504         this.hide();
22505                         
22506     },
22507     
22508     picker : function()
22509     {
22510         return this.pickerEl;
22511     },
22512     
22513     fillMonths: function()
22514     {    
22515         var i = 0;
22516         var months = this.picker().select('>.datepicker-months td', true).first();
22517         
22518         months.dom.innerHTML = '';
22519         
22520         while (i < 12) {
22521             var month = {
22522                 tag: 'span',
22523                 cls: 'month',
22524                 html: Roo.bootstrap.MonthField.dates[this.language].monthsShort[i++]
22525             };
22526             
22527             months.createChild(month);
22528         }
22529         
22530     },
22531     
22532     update: function()
22533     {
22534         var _this = this;
22535         
22536         if(typeof(this.vIndex) == 'undefined' && this.value.length){
22537             this.vIndex = Roo.bootstrap.MonthField.dates[this.language].months.indexOf(this.value);
22538         }
22539         
22540         Roo.each(this.pickerEl.select('> .datepicker-months tbody > tr > td > span', true).elements, function(e, k){
22541             e.removeClass('active');
22542             
22543             if(typeof(_this.vIndex) != 'undefined' && k == _this.vIndex){
22544                 e.addClass('active');
22545             }
22546         })
22547     },
22548     
22549     place: function()
22550     {
22551         if(this.isInline) {
22552             return;
22553         }
22554         
22555         this.picker().removeClass(['bottom', 'top']);
22556         
22557         if((Roo.lib.Dom.getViewHeight() + Roo.get(document.body).getScroll().top) - (this.inputEl().getBottom() + this.picker().getHeight()) < 0){
22558             /*
22559              * place to the top of element!
22560              *
22561              */
22562             
22563             this.picker().addClass('top');
22564             this.picker().setTop(this.inputEl().getTop() - this.picker().getHeight()).setLeft(this.inputEl().getLeft());
22565             
22566             return;
22567         }
22568         
22569         this.picker().addClass('bottom');
22570         
22571         this.picker().setTop(this.inputEl().getBottom()).setLeft(this.inputEl().getLeft());
22572     },
22573     
22574     onFocus : function()
22575     {
22576         Roo.bootstrap.MonthField.superclass.onFocus.call(this);
22577         this.show();
22578     },
22579     
22580     onBlur : function()
22581     {
22582         Roo.bootstrap.MonthField.superclass.onBlur.call(this);
22583         
22584         var d = this.inputEl().getValue();
22585         
22586         this.setValue(d);
22587                 
22588         this.hide();
22589     },
22590     
22591     show : function()
22592     {
22593         this.picker().show();
22594         this.picker().select('>.datepicker-months', true).first().show();
22595         this.update();
22596         this.place();
22597         
22598         this.fireEvent('show', this, this.date);
22599     },
22600     
22601     hide : function()
22602     {
22603         if(this.isInline) {
22604             return;
22605         }
22606         this.picker().hide();
22607         this.fireEvent('hide', this, this.date);
22608         
22609     },
22610     
22611     onMousedown: function(e)
22612     {
22613         e.stopPropagation();
22614         e.preventDefault();
22615     },
22616     
22617     keyup: function(e)
22618     {
22619         Roo.bootstrap.MonthField.superclass.keyup.call(this);
22620         this.update();
22621     },
22622
22623     fireKey: function(e)
22624     {
22625         if (!this.picker().isVisible()){
22626             if (e.keyCode == 27)   {// allow escape to hide and re-show picker
22627                 this.show();
22628             }
22629             return;
22630         }
22631         
22632         var dir;
22633         
22634         switch(e.keyCode){
22635             case 27: // escape
22636                 this.hide();
22637                 e.preventDefault();
22638                 break;
22639             case 37: // left
22640             case 39: // right
22641                 dir = e.keyCode == 37 ? -1 : 1;
22642                 
22643                 this.vIndex = this.vIndex + dir;
22644                 
22645                 if(this.vIndex < 0){
22646                     this.vIndex = 0;
22647                 }
22648                 
22649                 if(this.vIndex > 11){
22650                     this.vIndex = 11;
22651                 }
22652                 
22653                 if(isNaN(this.vIndex)){
22654                     this.vIndex = 0;
22655                 }
22656                 
22657                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22658                 
22659                 break;
22660             case 38: // up
22661             case 40: // down
22662                 
22663                 dir = e.keyCode == 38 ? -1 : 1;
22664                 
22665                 this.vIndex = this.vIndex + dir * 4;
22666                 
22667                 if(this.vIndex < 0){
22668                     this.vIndex = 0;
22669                 }
22670                 
22671                 if(this.vIndex > 11){
22672                     this.vIndex = 11;
22673                 }
22674                 
22675                 if(isNaN(this.vIndex)){
22676                     this.vIndex = 0;
22677                 }
22678                 
22679                 this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22680                 break;
22681                 
22682             case 13: // enter
22683                 
22684                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22685                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22686                 }
22687                 
22688                 this.hide();
22689                 e.preventDefault();
22690                 break;
22691             case 9: // tab
22692                 if(typeof(this.vIndex) != 'undefined' && !isNaN(this.vIndex)){
22693                     this.setValue(Roo.bootstrap.MonthField.dates[this.language].months[this.vIndex]);
22694                 }
22695                 this.hide();
22696                 break;
22697             case 16: // shift
22698             case 17: // ctrl
22699             case 18: // alt
22700                 break;
22701             default :
22702                 this.hide();
22703                 
22704         }
22705     },
22706     
22707     remove: function() 
22708     {
22709         this.picker().remove();
22710     }
22711    
22712 });
22713
22714 Roo.apply(Roo.bootstrap.MonthField,  {
22715     
22716     content : {
22717         tag: 'tbody',
22718         cn: [
22719         {
22720             tag: 'tr',
22721             cn: [
22722             {
22723                 tag: 'td',
22724                 colspan: '7'
22725             }
22726             ]
22727         }
22728         ]
22729     },
22730     
22731     dates:{
22732         en: {
22733             months: ["January", "February", "March", "April", "May", "June", "July", "August", "September", "October", "November", "December"],
22734             monthsShort: ["Jan", "Feb", "Mar", "Apr", "May", "Jun", "Jul", "Aug", "Sep", "Oct", "Nov", "Dec"]
22735         }
22736     }
22737 });
22738
22739 Roo.apply(Roo.bootstrap.MonthField,  {
22740   
22741     template : {
22742         tag: 'div',
22743         cls: 'datepicker dropdown-menu roo-dynamic',
22744         cn: [
22745             {
22746                 tag: 'div',
22747                 cls: 'datepicker-months',
22748                 cn: [
22749                 {
22750                     tag: 'table',
22751                     cls: 'table-condensed',
22752                     cn:[
22753                         Roo.bootstrap.DateField.content
22754                     ]
22755                 }
22756                 ]
22757             }
22758         ]
22759     }
22760 });
22761
22762  
22763
22764  
22765  /*
22766  * - LGPL
22767  *
22768  * CheckBox
22769  * 
22770  */
22771
22772 /**
22773  * @class Roo.bootstrap.CheckBox
22774  * @extends Roo.bootstrap.Input
22775  * Bootstrap CheckBox class
22776  * 
22777  * @cfg {String} valueOff The value that should go into the generated input element's value when unchecked.
22778  * @cfg {String} inputValue The value that should go into the generated input element's value when checked.
22779  * @cfg {String} boxLabel The text that appears beside the checkbox
22780  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the checkbox
22781  * @cfg {Boolean} checked initnal the element
22782  * @cfg {Boolean} inline inline the element (default false)
22783  * @cfg {String} groupId the checkbox group id // normal just use for checkbox
22784  * @cfg {String} tooltip label tooltip
22785  * 
22786  * @constructor
22787  * Create a new CheckBox
22788  * @param {Object} config The config object
22789  */
22790
22791 Roo.bootstrap.CheckBox = function(config){
22792     Roo.bootstrap.CheckBox.superclass.constructor.call(this, config);
22793    
22794     this.addEvents({
22795         /**
22796         * @event check
22797         * Fires when the element is checked or unchecked.
22798         * @param {Roo.bootstrap.CheckBox} this This input
22799         * @param {Boolean} checked The new checked value
22800         */
22801        check : true,
22802        /**
22803         * @event click
22804         * Fires when the element is click.
22805         * @param {Roo.bootstrap.CheckBox} this This input
22806         */
22807        click : true
22808     });
22809     
22810 };
22811
22812 Roo.extend(Roo.bootstrap.CheckBox, Roo.bootstrap.Input,  {
22813   
22814     inputType: 'checkbox',
22815     inputValue: 1,
22816     valueOff: 0,
22817     boxLabel: false,
22818     checked: false,
22819     weight : false,
22820     inline: false,
22821     tooltip : '',
22822     
22823     // checkbox success does not make any sense really.. 
22824     invalidClass : "",
22825     validClass : "",
22826     
22827     
22828     getAutoCreate : function()
22829     {
22830         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
22831         
22832         var id = Roo.id();
22833         
22834         var cfg = {};
22835         
22836         cfg.cls = 'form-group form-check ' + this.inputType; //input-group
22837         
22838         if(this.inline){
22839             cfg.cls += ' ' + this.inputType + '-inline  form-check-inline';
22840         }
22841         
22842         var input =  {
22843             tag: 'input',
22844             id : id,
22845             type : this.inputType,
22846             value : this.inputValue,
22847             cls : 'roo-' + this.inputType, //'form-box',
22848             placeholder : this.placeholder || ''
22849             
22850         };
22851         
22852         if(this.inputType != 'radio'){
22853             var hidden =  {
22854                 tag: 'input',
22855                 type : 'hidden',
22856                 cls : 'roo-hidden-value',
22857                 value : this.checked ? this.inputValue : this.valueOff
22858             };
22859         }
22860         
22861             
22862         if (this.weight) { // Validity check?
22863             cfg.cls += " " + this.inputType + "-" + this.weight;
22864         }
22865         
22866         if (this.disabled) {
22867             input.disabled=true;
22868         }
22869         
22870         if(this.checked){
22871             input.checked = this.checked;
22872         }
22873         
22874         if (this.name) {
22875             
22876             input.name = this.name;
22877             
22878             if(this.inputType != 'radio'){
22879                 hidden.name = this.name;
22880                 input.name = '_hidden_' + this.name;
22881             }
22882         }
22883         
22884         if (this.size) {
22885             input.cls += ' input-' + this.size;
22886         }
22887         
22888         var settings=this;
22889         
22890         ['xs','sm','md','lg'].map(function(size){
22891             if (settings[size]) {
22892                 cfg.cls += ' col-' + size + '-' + settings[size];
22893             }
22894         });
22895         
22896         var inputblock = input;
22897          
22898         if (this.before || this.after) {
22899             
22900             inputblock = {
22901                 cls : 'input-group',
22902                 cn :  [] 
22903             };
22904             
22905             if (this.before) {
22906                 inputblock.cn.push({
22907                     tag :'span',
22908                     cls : 'input-group-addon',
22909                     html : this.before
22910                 });
22911             }
22912             
22913             inputblock.cn.push(input);
22914             
22915             if(this.inputType != 'radio'){
22916                 inputblock.cn.push(hidden);
22917             }
22918             
22919             if (this.after) {
22920                 inputblock.cn.push({
22921                     tag :'span',
22922                     cls : 'input-group-addon',
22923                     html : this.after
22924                 });
22925             }
22926             
22927         }
22928         var boxLabelCfg = false;
22929         
22930         if(this.boxLabel){
22931            
22932             boxLabelCfg = {
22933                 tag: 'label',
22934                 //'for': id, // box label is handled by onclick - so no for...
22935                 cls: 'box-label',
22936                 html: this.boxLabel
22937             };
22938             if(this.tooltip){
22939                 boxLabelCfg.tooltip = this.tooltip;
22940             }
22941              
22942         }
22943         
22944         
22945         if (align ==='left' && this.fieldLabel.length) {
22946 //                Roo.log("left and has label");
22947             cfg.cn = [
22948                 {
22949                     tag: 'label',
22950                     'for' :  id,
22951                     cls : 'control-label',
22952                     html : this.fieldLabel
22953                 },
22954                 {
22955                     cls : "", 
22956                     cn: [
22957                         inputblock
22958                     ]
22959                 }
22960             ];
22961             
22962             if (boxLabelCfg) {
22963                 cfg.cn[1].cn.push(boxLabelCfg);
22964             }
22965             
22966             if(this.labelWidth > 12){
22967                 cfg.cn[0].style = "width: " + this.labelWidth + 'px';
22968             }
22969             
22970             if(this.labelWidth < 13 && this.labelmd == 0){
22971                 this.labelmd = this.labelWidth;
22972             }
22973             
22974             if(this.labellg > 0){
22975                 cfg.cn[0].cls += ' col-lg-' + this.labellg;
22976                 cfg.cn[1].cls += ' col-lg-' + (12 - this.labellg);
22977             }
22978             
22979             if(this.labelmd > 0){
22980                 cfg.cn[0].cls += ' col-md-' + this.labelmd;
22981                 cfg.cn[1].cls += ' col-md-' + (12 - this.labelmd);
22982             }
22983             
22984             if(this.labelsm > 0){
22985                 cfg.cn[0].cls += ' col-sm-' + this.labelsm;
22986                 cfg.cn[1].cls += ' col-sm-' + (12 - this.labelsm);
22987             }
22988             
22989             if(this.labelxs > 0){
22990                 cfg.cn[0].cls += ' col-xs-' + this.labelxs;
22991                 cfg.cn[1].cls += ' col-xs-' + (12 - this.labelxs);
22992             }
22993             
22994         } else if ( this.fieldLabel.length) {
22995 //                Roo.log(" label");
22996                 cfg.cn = [
22997                    
22998                     {
22999                         tag: this.boxLabel ? 'span' : 'label',
23000                         'for': id,
23001                         cls: 'control-label box-input-label',
23002                         //cls : 'input-group-addon',
23003                         html : this.fieldLabel
23004                     },
23005                     
23006                     inputblock
23007                     
23008                 ];
23009                 if (boxLabelCfg) {
23010                     cfg.cn.push(boxLabelCfg);
23011                 }
23012
23013         } else {
23014             
23015 //                Roo.log(" no label && no align");
23016                 cfg.cn = [  inputblock ] ;
23017                 if (boxLabelCfg) {
23018                     cfg.cn.push(boxLabelCfg);
23019                 }
23020
23021                 
23022         }
23023         
23024        
23025         
23026         if(this.inputType != 'radio'){
23027             cfg.cn.push(hidden);
23028         }
23029         
23030         return cfg;
23031         
23032     },
23033     
23034     /**
23035      * return the real input element.
23036      */
23037     inputEl: function ()
23038     {
23039         return this.el.select('input.roo-' + this.inputType,true).first();
23040     },
23041     hiddenEl: function ()
23042     {
23043         return this.el.select('input.roo-hidden-value',true).first();
23044     },
23045     
23046     labelEl: function()
23047     {
23048         return this.el.select('label.control-label',true).first();
23049     },
23050     /* depricated... */
23051     
23052     label: function()
23053     {
23054         return this.labelEl();
23055     },
23056     
23057     boxLabelEl: function()
23058     {
23059         return this.el.select('label.box-label',true).first();
23060     },
23061     
23062     initEvents : function()
23063     {
23064 //        Roo.bootstrap.CheckBox.superclass.initEvents.call(this);
23065         
23066         this.inputEl().on('click', this.onClick,  this);
23067         
23068         if (this.boxLabel) { 
23069             this.el.select('label.box-label',true).first().on('click', this.onClick,  this);
23070         }
23071         
23072         this.startValue = this.getValue();
23073         
23074         if(this.groupId){
23075             Roo.bootstrap.CheckBox.register(this);
23076         }
23077     },
23078     
23079     onClick : function(e)
23080     {   
23081         if(this.fireEvent('click', this, e) !== false){
23082             this.setChecked(!this.checked);
23083         }
23084         
23085     },
23086     
23087     setChecked : function(state,suppressEvent)
23088     {
23089         this.startValue = this.getValue();
23090
23091         if(this.inputType == 'radio'){
23092             
23093             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23094                 e.dom.checked = false;
23095             });
23096             
23097             this.inputEl().dom.checked = true;
23098             
23099             this.inputEl().dom.value = this.inputValue;
23100             
23101             if(suppressEvent !== true){
23102                 this.fireEvent('check', this, true);
23103             }
23104             
23105             this.validate();
23106             
23107             return;
23108         }
23109         
23110         this.checked = state;
23111         
23112         this.inputEl().dom.checked = state;
23113         
23114         
23115         this.hiddenEl().dom.value = state ? this.inputValue : this.valueOff;
23116         
23117         if(suppressEvent !== true){
23118             this.fireEvent('check', this, state);
23119         }
23120         
23121         this.validate();
23122     },
23123     
23124     getValue : function()
23125     {
23126         if(this.inputType == 'radio'){
23127             return this.getGroupValue();
23128         }
23129         
23130         return this.hiddenEl().dom.value;
23131         
23132     },
23133     
23134     getGroupValue : function()
23135     {
23136         if(typeof(this.el.up('form').child('input[name='+this.name+']:checked', true)) == 'undefined'){
23137             return '';
23138         }
23139         
23140         return this.el.up('form').child('input[name='+this.name+']:checked', true).value;
23141     },
23142     
23143     setValue : function(v,suppressEvent)
23144     {
23145         if(this.inputType == 'radio'){
23146             this.setGroupValue(v, suppressEvent);
23147             return;
23148         }
23149         
23150         this.setChecked(((typeof(v) == 'undefined') ? this.checked : (String(v) === String(this.inputValue))), suppressEvent);
23151         
23152         this.validate();
23153     },
23154     
23155     setGroupValue : function(v, suppressEvent)
23156     {
23157         this.startValue = this.getValue();
23158         
23159         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23160             e.dom.checked = false;
23161             
23162             if(e.dom.value == v){
23163                 e.dom.checked = true;
23164             }
23165         });
23166         
23167         if(suppressEvent !== true){
23168             this.fireEvent('check', this, true);
23169         }
23170
23171         this.validate();
23172         
23173         return;
23174     },
23175     
23176     validate : function()
23177     {
23178         if(this.getVisibilityEl().hasClass('hidden')){
23179             return true;
23180         }
23181         
23182         if(
23183                 this.disabled || 
23184                 (this.inputType == 'radio' && this.validateRadio()) ||
23185                 (this.inputType == 'checkbox' && this.validateCheckbox())
23186         ){
23187             this.markValid();
23188             return true;
23189         }
23190         
23191         this.markInvalid();
23192         return false;
23193     },
23194     
23195     validateRadio : function()
23196     {
23197         if(this.getVisibilityEl().hasClass('hidden')){
23198             return true;
23199         }
23200         
23201         if(this.allowBlank){
23202             return true;
23203         }
23204         
23205         var valid = false;
23206         
23207         Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23208             if(!e.dom.checked){
23209                 return;
23210             }
23211             
23212             valid = true;
23213             
23214             return false;
23215         });
23216         
23217         return valid;
23218     },
23219     
23220     validateCheckbox : function()
23221     {
23222         if(!this.groupId){
23223             return (this.getValue() == this.inputValue || this.allowBlank) ? true : false;
23224             //return (this.getValue() == this.inputValue) ? true : false;
23225         }
23226         
23227         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23228         
23229         if(!group){
23230             return false;
23231         }
23232         
23233         var r = false;
23234         
23235         for(var i in group){
23236             if(group[i].el.isVisible(true)){
23237                 r = false;
23238                 break;
23239             }
23240             
23241             r = true;
23242         }
23243         
23244         for(var i in group){
23245             if(r){
23246                 break;
23247             }
23248             
23249             r = (group[i].getValue() == group[i].inputValue) ? true : false;
23250         }
23251         
23252         return r;
23253     },
23254     
23255     /**
23256      * Mark this field as valid
23257      */
23258     markValid : function()
23259     {
23260         var _this = this;
23261         
23262         this.fireEvent('valid', this);
23263         
23264         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23265         
23266         if(this.groupId){
23267             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23268         }
23269         
23270         if(label){
23271             label.markValid();
23272         }
23273
23274         if(this.inputType == 'radio'){
23275             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23276                 var fg = e.findParent('.form-group', false, true);
23277                 if (Roo.bootstrap.version == 3) {
23278                     fg.removeClass([_this.invalidClass, _this.validClass]);
23279                     fg.addClass(_this.validClass);
23280                 } else {
23281                     fg.removeClass(['is-valid', 'is-invalid']);
23282                     fg.addClass('is-valid');
23283                 }
23284             });
23285             
23286             return;
23287         }
23288
23289         if(!this.groupId){
23290             var fg = this.el.findParent('.form-group', false, true);
23291             if (Roo.bootstrap.version == 3) {
23292                 fg.removeClass([this.invalidClass, this.validClass]);
23293                 fg.addClass(this.validClass);
23294             } else {
23295                 fg.removeClass(['is-valid', 'is-invalid']);
23296                 fg.addClass('is-valid');
23297             }
23298             return;
23299         }
23300         
23301         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23302         
23303         if(!group){
23304             return;
23305         }
23306         
23307         for(var i in group){
23308             var fg = group[i].el.findParent('.form-group', false, true);
23309             if (Roo.bootstrap.version == 3) {
23310                 fg.removeClass([this.invalidClass, this.validClass]);
23311                 fg.addClass(this.validClass);
23312             } else {
23313                 fg.removeClass(['is-valid', 'is-invalid']);
23314                 fg.addClass('is-valid');
23315             }
23316         }
23317     },
23318     
23319      /**
23320      * Mark this field as invalid
23321      * @param {String} msg The validation message
23322      */
23323     markInvalid : function(msg)
23324     {
23325         if(this.allowBlank){
23326             return;
23327         }
23328         
23329         var _this = this;
23330         
23331         this.fireEvent('invalid', this, msg);
23332         
23333         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23334         
23335         if(this.groupId){
23336             label = Roo.bootstrap.FieldLabel.get(this.groupId + '-group');
23337         }
23338         
23339         if(label){
23340             label.markInvalid();
23341         }
23342             
23343         if(this.inputType == 'radio'){
23344             
23345             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23346                 var fg = e.findParent('.form-group', false, true);
23347                 if (Roo.bootstrap.version == 3) {
23348                     fg.removeClass([_this.invalidClass, _this.validClass]);
23349                     fg.addClass(_this.invalidClass);
23350                 } else {
23351                     fg.removeClass(['is-invalid', 'is-valid']);
23352                     fg.addClass('is-invalid');
23353                 }
23354             });
23355             
23356             return;
23357         }
23358         
23359         if(!this.groupId){
23360             var fg = this.el.findParent('.form-group', false, true);
23361             if (Roo.bootstrap.version == 3) {
23362                 fg.removeClass([_this.invalidClass, _this.validClass]);
23363                 fg.addClass(_this.invalidClass);
23364             } else {
23365                 fg.removeClass(['is-invalid', 'is-valid']);
23366                 fg.addClass('is-invalid');
23367             }
23368             return;
23369         }
23370         
23371         var group = Roo.bootstrap.CheckBox.get(this.groupId);
23372         
23373         if(!group){
23374             return;
23375         }
23376         
23377         for(var i in group){
23378             var fg = group[i].el.findParent('.form-group', false, true);
23379             if (Roo.bootstrap.version == 3) {
23380                 fg.removeClass([_this.invalidClass, _this.validClass]);
23381                 fg.addClass(_this.invalidClass);
23382             } else {
23383                 fg.removeClass(['is-invalid', 'is-valid']);
23384                 fg.addClass('is-invalid');
23385             }
23386         }
23387         
23388     },
23389     
23390     clearInvalid : function()
23391     {
23392         Roo.bootstrap.Input.prototype.clearInvalid.call(this);
23393         
23394         // this.el.findParent('.form-group', false, true).removeClass([this.invalidClass, this.validClass]);
23395         
23396         var label = Roo.bootstrap.FieldLabel.get(this.name + '-group');
23397         
23398         if (label && label.iconEl) {
23399             label.iconEl.removeClass([ label.validClass, label.invalidClass ]);
23400             label.iconEl.removeClass(['is-invalid', 'is-valid']);
23401         }
23402     },
23403     
23404     disable : function()
23405     {
23406         if(this.inputType != 'radio'){
23407             Roo.bootstrap.CheckBox.superclass.disable.call(this);
23408             return;
23409         }
23410         
23411         var _this = this;
23412         
23413         if(this.rendered){
23414             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23415                 _this.getActionEl().addClass(this.disabledClass);
23416                 e.dom.disabled = true;
23417             });
23418         }
23419         
23420         this.disabled = true;
23421         this.fireEvent("disable", this);
23422         return this;
23423     },
23424
23425     enable : function()
23426     {
23427         if(this.inputType != 'radio'){
23428             Roo.bootstrap.CheckBox.superclass.enable.call(this);
23429             return;
23430         }
23431         
23432         var _this = this;
23433         
23434         if(this.rendered){
23435             Roo.each(this.el.up('form').select('input[name='+this.name+']', true).elements, function(e){
23436                 _this.getActionEl().removeClass(this.disabledClass);
23437                 e.dom.disabled = false;
23438             });
23439         }
23440         
23441         this.disabled = false;
23442         this.fireEvent("enable", this);
23443         return this;
23444     },
23445     
23446     setBoxLabel : function(v)
23447     {
23448         this.boxLabel = v;
23449         
23450         if(this.rendered){
23451             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23452         }
23453     }
23454
23455 });
23456
23457 Roo.apply(Roo.bootstrap.CheckBox, {
23458     
23459     groups: {},
23460     
23461      /**
23462     * register a CheckBox Group
23463     * @param {Roo.bootstrap.CheckBox} the CheckBox to add
23464     */
23465     register : function(checkbox)
23466     {
23467         if(typeof(this.groups[checkbox.groupId]) == 'undefined'){
23468             this.groups[checkbox.groupId] = {};
23469         }
23470         
23471         if(this.groups[checkbox.groupId].hasOwnProperty(checkbox.name)){
23472             return;
23473         }
23474         
23475         this.groups[checkbox.groupId][checkbox.name] = checkbox;
23476         
23477     },
23478     /**
23479     * fetch a CheckBox Group based on the group ID
23480     * @param {string} the group ID
23481     * @returns {Roo.bootstrap.CheckBox} the CheckBox group
23482     */
23483     get: function(groupId) {
23484         if (typeof(this.groups[groupId]) == 'undefined') {
23485             return false;
23486         }
23487         
23488         return this.groups[groupId] ;
23489     }
23490     
23491     
23492 });
23493 /*
23494  * - LGPL
23495  *
23496  * RadioItem
23497  * 
23498  */
23499
23500 /**
23501  * @class Roo.bootstrap.Radio
23502  * @extends Roo.bootstrap.Component
23503  * Bootstrap Radio class
23504  * @cfg {String} boxLabel - the label associated
23505  * @cfg {String} value - the value of radio
23506  * 
23507  * @constructor
23508  * Create a new Radio
23509  * @param {Object} config The config object
23510  */
23511 Roo.bootstrap.Radio = function(config){
23512     Roo.bootstrap.Radio.superclass.constructor.call(this, config);
23513     
23514 };
23515
23516 Roo.extend(Roo.bootstrap.Radio, Roo.bootstrap.Component, {
23517     
23518     boxLabel : '',
23519     
23520     value : '',
23521     
23522     getAutoCreate : function()
23523     {
23524         var cfg = {
23525             tag : 'div',
23526             cls : 'form-group radio',
23527             cn : [
23528                 {
23529                     tag : 'label',
23530                     cls : 'box-label',
23531                     html : this.boxLabel
23532                 }
23533             ]
23534         };
23535         
23536         return cfg;
23537     },
23538     
23539     initEvents : function() 
23540     {
23541         this.parent().register(this);
23542         
23543         this.el.on('click', this.onClick, this);
23544         
23545     },
23546     
23547     onClick : function(e)
23548     {
23549         if(this.parent().fireEvent('click', this.parent(), this, e) !== false){
23550             this.setChecked(true);
23551         }
23552     },
23553     
23554     setChecked : function(state, suppressEvent)
23555     {
23556         this.parent().setValue(this.value, suppressEvent);
23557         
23558     },
23559     
23560     setBoxLabel : function(v)
23561     {
23562         this.boxLabel = v;
23563         
23564         if(this.rendered){
23565             this.el.select('label.box-label',true).first().dom.innerHTML = (v === null || v === undefined ? '' : v);
23566         }
23567     }
23568     
23569 });
23570  
23571
23572  /*
23573  * - LGPL
23574  *
23575  * Input
23576  * 
23577  */
23578
23579 /**
23580  * @class Roo.bootstrap.SecurePass
23581  * @extends Roo.bootstrap.Input
23582  * Bootstrap SecurePass class
23583  *
23584  * 
23585  * @constructor
23586  * Create a new SecurePass
23587  * @param {Object} config The config object
23588  */
23589  
23590 Roo.bootstrap.SecurePass = function (config) {
23591     // these go here, so the translation tool can replace them..
23592     this.errors = {
23593         PwdEmpty: "Please type a password, and then retype it to confirm.",
23594         PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23595         PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23596         PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23597         IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23598         FNInPwd: "Your password can't contain your first name. Please type a different password.",
23599         LNInPwd: "Your password can't contain your last name. Please type a different password.",
23600         TooWeak: "Your password is Too Weak."
23601     },
23602     this.meterLabel = "Password strength:";
23603     this.pwdStrengths = ["Too Weak", "Weak", "Medium", "Strong"];
23604     this.meterClass = [
23605         "roo-password-meter-tooweak", 
23606         "roo-password-meter-weak", 
23607         "roo-password-meter-medium", 
23608         "roo-password-meter-strong", 
23609         "roo-password-meter-grey"
23610     ];
23611     
23612     this.errors = {};
23613     
23614     Roo.bootstrap.SecurePass.superclass.constructor.call(this, config);
23615 }
23616
23617 Roo.extend(Roo.bootstrap.SecurePass, Roo.bootstrap.Input, {
23618     /**
23619      * @cfg {String/Object} errors A Error spec, or true for a default spec (defaults to
23620      * {
23621      *  PwdEmpty: "Please type a password, and then retype it to confirm.",
23622      *  PwdShort: "Your password must be at least 6 characters long. Please type a different password.",
23623      *  PwdLong: "Your password can't contain more than 16 characters. Please type a different password.",
23624      *  PwdBadChar: "The password contains characters that aren't allowed. Please type a different password.",
23625      *  IDInPwd: "Your password can't include the part of your ID. Please type a different password.",
23626      *  FNInPwd: "Your password can't contain your first name. Please type a different password.",
23627      *  LNInPwd: "Your password can't contain your last name. Please type a different password."
23628      * })
23629      */
23630     // private
23631     
23632     meterWidth: 300,
23633     errorMsg :'',    
23634     errors: false,
23635     imageRoot: '/',
23636     /**
23637      * @cfg {String/Object} Label for the strength meter (defaults to
23638      * 'Password strength:')
23639      */
23640     // private
23641     meterLabel: '',
23642     /**
23643      * @cfg {String/Object} pwdStrengths A pwdStrengths spec, or true for a default spec (defaults to
23644      * ['Weak', 'Medium', 'Strong'])
23645      */
23646     // private    
23647     pwdStrengths: false,    
23648     // private
23649     strength: 0,
23650     // private
23651     _lastPwd: null,
23652     // private
23653     kCapitalLetter: 0,
23654     kSmallLetter: 1,
23655     kDigit: 2,
23656     kPunctuation: 3,
23657     
23658     insecure: false,
23659     // private
23660     initEvents: function ()
23661     {
23662         Roo.bootstrap.SecurePass.superclass.initEvents.call(this);
23663
23664         if (this.el.is('input[type=password]') && Roo.isSafari) {
23665             this.el.on('keydown', this.SafariOnKeyDown, this);
23666         }
23667
23668         this.el.on('keyup', this.checkStrength, this, {buffer: 50});
23669     },
23670     // private
23671     onRender: function (ct, position)
23672     {
23673         Roo.bootstrap.SecurePass.superclass.onRender.call(this, ct, position);
23674         this.wrap = this.el.wrap({cls: 'x-form-field-wrap'});
23675         this.trigger = this.wrap.createChild({tag: 'div', cls: 'StrengthMeter ' + this.triggerClass});
23676
23677         this.trigger.createChild({
23678                    cn: [
23679                     {
23680                     //id: 'PwdMeter',
23681                     tag: 'div',
23682                     cls: 'roo-password-meter-grey col-xs-12',
23683                     style: {
23684                         //width: 0,
23685                         //width: this.meterWidth + 'px'                                                
23686                         }
23687                     },
23688                     {                            
23689                          cls: 'roo-password-meter-text'                          
23690                     }
23691                 ]            
23692         });
23693
23694          
23695         if (this.hideTrigger) {
23696             this.trigger.setDisplayed(false);
23697         }
23698         this.setSize(this.width || '', this.height || '');
23699     },
23700     // private
23701     onDestroy: function ()
23702     {
23703         if (this.trigger) {
23704             this.trigger.removeAllListeners();
23705             this.trigger.remove();
23706         }
23707         if (this.wrap) {
23708             this.wrap.remove();
23709         }
23710         Roo.bootstrap.TriggerField.superclass.onDestroy.call(this);
23711     },
23712     // private
23713     checkStrength: function ()
23714     {
23715         var pwd = this.inputEl().getValue();
23716         if (pwd == this._lastPwd) {
23717             return;
23718         }
23719
23720         var strength;
23721         if (this.ClientSideStrongPassword(pwd)) {
23722             strength = 3;
23723         } else if (this.ClientSideMediumPassword(pwd)) {
23724             strength = 2;
23725         } else if (this.ClientSideWeakPassword(pwd)) {
23726             strength = 1;
23727         } else {
23728             strength = 0;
23729         }
23730         
23731         Roo.log('strength1: ' + strength);
23732         
23733         //var pm = this.trigger.child('div/div/div').dom;
23734         var pm = this.trigger.child('div/div');
23735         pm.removeClass(this.meterClass);
23736         pm.addClass(this.meterClass[strength]);
23737                 
23738         
23739         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23740                 
23741         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23742         
23743         this._lastPwd = pwd;
23744     },
23745     reset: function ()
23746     {
23747         Roo.bootstrap.SecurePass.superclass.reset.call(this);
23748         
23749         this._lastPwd = '';
23750         
23751         var pm = this.trigger.child('div/div');
23752         pm.removeClass(this.meterClass);
23753         pm.addClass('roo-password-meter-grey');        
23754         
23755         
23756         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23757         
23758         pt.innerHTML = '';
23759         this.inputEl().dom.type='password';
23760     },
23761     // private
23762     validateValue: function (value)
23763     {
23764         if (!Roo.bootstrap.SecurePass.superclass.validateValue.call(this, value)) {
23765             return false;
23766         }
23767         if (value.length == 0) {
23768             if (this.allowBlank) {
23769                 this.clearInvalid();
23770                 return true;
23771             }
23772
23773             this.markInvalid(this.errors.PwdEmpty);
23774             this.errorMsg = this.errors.PwdEmpty;
23775             return false;
23776         }
23777         
23778         if(this.insecure){
23779             return true;
23780         }
23781         
23782         if (!value.match(/[\x21-\x7e]+/)) {
23783             this.markInvalid(this.errors.PwdBadChar);
23784             this.errorMsg = this.errors.PwdBadChar;
23785             return false;
23786         }
23787         if (value.length < 6) {
23788             this.markInvalid(this.errors.PwdShort);
23789             this.errorMsg = this.errors.PwdShort;
23790             return false;
23791         }
23792         if (value.length > 16) {
23793             this.markInvalid(this.errors.PwdLong);
23794             this.errorMsg = this.errors.PwdLong;
23795             return false;
23796         }
23797         var strength;
23798         if (this.ClientSideStrongPassword(value)) {
23799             strength = 3;
23800         } else if (this.ClientSideMediumPassword(value)) {
23801             strength = 2;
23802         } else if (this.ClientSideWeakPassword(value)) {
23803             strength = 1;
23804         } else {
23805             strength = 0;
23806         }
23807
23808         
23809         if (strength < 2) {
23810             //this.markInvalid(this.errors.TooWeak);
23811             this.errorMsg = this.errors.TooWeak;
23812             //return false;
23813         }
23814         
23815         
23816         console.log('strength2: ' + strength);
23817         
23818         //var pm = this.trigger.child('div/div/div').dom;
23819         
23820         var pm = this.trigger.child('div/div');
23821         pm.removeClass(this.meterClass);
23822         pm.addClass(this.meterClass[strength]);
23823                 
23824         var pt = this.trigger.child('/div').child('>*[class=roo-password-meter-text]').dom;        
23825                 
23826         pt.innerHTML = this.meterLabel + '&nbsp;' + this.pwdStrengths[strength];
23827         
23828         this.errorMsg = ''; 
23829         return true;
23830     },
23831     // private
23832     CharacterSetChecks: function (type)
23833     {
23834         this.type = type;
23835         this.fResult = false;
23836     },
23837     // private
23838     isctype: function (character, type)
23839     {
23840         switch (type) {  
23841             case this.kCapitalLetter:
23842                 if (character >= 'A' && character <= 'Z') {
23843                     return true;
23844                 }
23845                 break;
23846             
23847             case this.kSmallLetter:
23848                 if (character >= 'a' && character <= 'z') {
23849                     return true;
23850                 }
23851                 break;
23852             
23853             case this.kDigit:
23854                 if (character >= '0' && character <= '9') {
23855                     return true;
23856                 }
23857                 break;
23858             
23859             case this.kPunctuation:
23860                 if ('!@#$%^&*()_+-=\'";:[{]}|.>,</?`~'.indexOf(character) >= 0) {
23861                     return true;
23862                 }
23863                 break;
23864             
23865             default:
23866                 return false;
23867         }
23868
23869     },
23870     // private
23871     IsLongEnough: function (pwd, size)
23872     {
23873         return !(pwd == null || isNaN(size) || pwd.length < size);
23874     },
23875     // private
23876     SpansEnoughCharacterSets: function (word, nb)
23877     {
23878         if (!this.IsLongEnough(word, nb))
23879         {
23880             return false;
23881         }
23882
23883         var characterSetChecks = new Array(
23884             new this.CharacterSetChecks(this.kCapitalLetter), new this.CharacterSetChecks(this.kSmallLetter),
23885             new this.CharacterSetChecks(this.kDigit), new this.CharacterSetChecks(this.kPunctuation)
23886         );
23887         
23888         for (var index = 0; index < word.length; ++index) {
23889             for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23890                 if (!characterSetChecks[nCharSet].fResult && this.isctype(word.charAt(index), characterSetChecks[nCharSet].type)) {
23891                     characterSetChecks[nCharSet].fResult = true;
23892                     break;
23893                 }
23894             }
23895         }
23896
23897         var nCharSets = 0;
23898         for (var nCharSet = 0; nCharSet < characterSetChecks.length; ++nCharSet) {
23899             if (characterSetChecks[nCharSet].fResult) {
23900                 ++nCharSets;
23901             }
23902         }
23903
23904         if (nCharSets < nb) {
23905             return false;
23906         }
23907         return true;
23908     },
23909     // private
23910     ClientSideStrongPassword: function (pwd)
23911     {
23912         return this.IsLongEnough(pwd, 8) && this.SpansEnoughCharacterSets(pwd, 3);
23913     },
23914     // private
23915     ClientSideMediumPassword: function (pwd)
23916     {
23917         return this.IsLongEnough(pwd, 7) && this.SpansEnoughCharacterSets(pwd, 2);
23918     },
23919     // private
23920     ClientSideWeakPassword: function (pwd)
23921     {
23922         return this.IsLongEnough(pwd, 6) || !this.IsLongEnough(pwd, 0);
23923     }
23924           
23925 })//<script type="text/javascript">
23926
23927 /*
23928  * Based  Ext JS Library 1.1.1
23929  * Copyright(c) 2006-2007, Ext JS, LLC.
23930  * LGPL
23931  *
23932  */
23933  
23934 /**
23935  * @class Roo.HtmlEditorCore
23936  * @extends Roo.Component
23937  * Provides a the editing component for the HTML editors in Roo. (bootstrap and Roo.form)
23938  *
23939  * any element that has display set to 'none' can cause problems in Safari and Firefox.<br/><br/>
23940  */
23941
23942 Roo.HtmlEditorCore = function(config){
23943     
23944     
23945     Roo.HtmlEditorCore.superclass.constructor.call(this, config);
23946     
23947     
23948     this.addEvents({
23949         /**
23950          * @event initialize
23951          * Fires when the editor is fully initialized (including the iframe)
23952          * @param {Roo.HtmlEditorCore} this
23953          */
23954         initialize: true,
23955         /**
23956          * @event activate
23957          * Fires when the editor is first receives the focus. Any insertion must wait
23958          * until after this event.
23959          * @param {Roo.HtmlEditorCore} this
23960          */
23961         activate: true,
23962          /**
23963          * @event beforesync
23964          * Fires before the textarea is updated with content from the editor iframe. Return false
23965          * to cancel the sync.
23966          * @param {Roo.HtmlEditorCore} this
23967          * @param {String} html
23968          */
23969         beforesync: true,
23970          /**
23971          * @event beforepush
23972          * Fires before the iframe editor is updated with content from the textarea. Return false
23973          * to cancel the push.
23974          * @param {Roo.HtmlEditorCore} this
23975          * @param {String} html
23976          */
23977         beforepush: true,
23978          /**
23979          * @event sync
23980          * Fires when the textarea is updated with content from the editor iframe.
23981          * @param {Roo.HtmlEditorCore} this
23982          * @param {String} html
23983          */
23984         sync: true,
23985          /**
23986          * @event push
23987          * Fires when the iframe editor is updated with content from the textarea.
23988          * @param {Roo.HtmlEditorCore} this
23989          * @param {String} html
23990          */
23991         push: true,
23992         
23993         /**
23994          * @event editorevent
23995          * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
23996          * @param {Roo.HtmlEditorCore} this
23997          */
23998         editorevent: true
23999         
24000     });
24001     
24002     // at this point this.owner is set, so we can start working out the whitelisted / blacklisted elements
24003     
24004     // defaults : white / black...
24005     this.applyBlacklists();
24006     
24007     
24008     
24009 };
24010
24011
24012 Roo.extend(Roo.HtmlEditorCore, Roo.Component,  {
24013
24014
24015      /**
24016      * @cfg {Roo.form.HtmlEditor|Roo.bootstrap.HtmlEditor} the owner field 
24017      */
24018     
24019     owner : false,
24020     
24021      /**
24022      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
24023      *                        Roo.resizable.
24024      */
24025     resizable : false,
24026      /**
24027      * @cfg {Number} height (in pixels)
24028      */   
24029     height: 300,
24030    /**
24031      * @cfg {Number} width (in pixels)
24032      */   
24033     width: 500,
24034     
24035     /**
24036      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
24037      * 
24038      */
24039     stylesheets: false,
24040     
24041     // id of frame..
24042     frameId: false,
24043     
24044     // private properties
24045     validationEvent : false,
24046     deferHeight: true,
24047     initialized : false,
24048     activated : false,
24049     sourceEditMode : false,
24050     onFocus : Roo.emptyFn,
24051     iframePad:3,
24052     hideMode:'offsets',
24053     
24054     clearUp: true,
24055     
24056     // blacklist + whitelisted elements..
24057     black: false,
24058     white: false,
24059      
24060     bodyCls : '',
24061
24062     /**
24063      * Protected method that will not generally be called directly. It
24064      * is called when the editor initializes the iframe with HTML contents. Override this method if you
24065      * want to change the initialization markup of the iframe (e.g. to add stylesheets).
24066      */
24067     getDocMarkup : function(){
24068         // body styles..
24069         var st = '';
24070         
24071         // inherit styels from page...?? 
24072         if (this.stylesheets === false) {
24073             
24074             Roo.get(document.head).select('style').each(function(node) {
24075                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24076             });
24077             
24078             Roo.get(document.head).select('link').each(function(node) { 
24079                 st += node.dom.outerHTML || new XMLSerializer().serializeToString(node.dom);
24080             });
24081             
24082         } else if (!this.stylesheets.length) {
24083                 // simple..
24084                 st = '<style type="text/css">' +
24085                     'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24086                    '</style>';
24087         } else {
24088             for (var i in this.stylesheets) { 
24089                 st += '<link rel="stylesheet" href="' + this.stylesheets[i] +'" type="text/css">';
24090             }
24091             
24092         }
24093         
24094         st +=  '<style type="text/css">' +
24095             'IMG { cursor: pointer } ' +
24096         '</style>';
24097
24098         var cls = 'roo-htmleditor-body';
24099         
24100         if(this.bodyCls.length){
24101             cls += ' ' + this.bodyCls;
24102         }
24103         
24104         return '<html><head>' + st  +
24105             //<style type="text/css">' +
24106             //'body{border:0;margin:0;padding:3px;height:98%;cursor:text;}' +
24107             //'</style>' +
24108             ' </head><body contenteditable="true" data-enable-grammerly="true" class="' +  cls + '"></body></html>';
24109     },
24110
24111     // private
24112     onRender : function(ct, position)
24113     {
24114         var _t = this;
24115         //Roo.HtmlEditorCore.superclass.onRender.call(this, ct, position);
24116         this.el = this.owner.inputEl ? this.owner.inputEl() : this.owner.el;
24117         
24118         
24119         this.el.dom.style.border = '0 none';
24120         this.el.dom.setAttribute('tabIndex', -1);
24121         this.el.addClass('x-hidden hide');
24122         
24123         
24124         
24125         if(Roo.isIE){ // fix IE 1px bogus margin
24126             this.el.applyStyles('margin-top:-1px;margin-bottom:-1px;')
24127         }
24128        
24129         
24130         this.frameId = Roo.id();
24131         
24132          
24133         
24134         var iframe = this.owner.wrap.createChild({
24135             tag: 'iframe',
24136             cls: 'form-control', // bootstrap..
24137             id: this.frameId,
24138             name: this.frameId,
24139             frameBorder : 'no',
24140             'src' : Roo.SSL_SECURE_URL ? Roo.SSL_SECURE_URL  :  "javascript:false"
24141         }, this.el
24142         );
24143         
24144         
24145         this.iframe = iframe.dom;
24146
24147          this.assignDocWin();
24148         
24149         this.doc.designMode = 'on';
24150        
24151         this.doc.open();
24152         this.doc.write(this.getDocMarkup());
24153         this.doc.close();
24154
24155         
24156         var task = { // must defer to wait for browser to be ready
24157             run : function(){
24158                 //console.log("run task?" + this.doc.readyState);
24159                 this.assignDocWin();
24160                 if(this.doc.body || this.doc.readyState == 'complete'){
24161                     try {
24162                         this.doc.designMode="on";
24163                     } catch (e) {
24164                         return;
24165                     }
24166                     Roo.TaskMgr.stop(task);
24167                     this.initEditor.defer(10, this);
24168                 }
24169             },
24170             interval : 10,
24171             duration: 10000,
24172             scope: this
24173         };
24174         Roo.TaskMgr.start(task);
24175
24176     },
24177
24178     // private
24179     onResize : function(w, h)
24180     {
24181          Roo.log('resize: ' +w + ',' + h );
24182         //Roo.HtmlEditorCore.superclass.onResize.apply(this, arguments);
24183         if(!this.iframe){
24184             return;
24185         }
24186         if(typeof w == 'number'){
24187             
24188             this.iframe.style.width = w + 'px';
24189         }
24190         if(typeof h == 'number'){
24191             
24192             this.iframe.style.height = h + 'px';
24193             if(this.doc){
24194                 (this.doc.body || this.doc.documentElement).style.height = (h - (this.iframePad*2)) + 'px';
24195             }
24196         }
24197         
24198     },
24199
24200     /**
24201      * Toggles the editor between standard and source edit mode.
24202      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
24203      */
24204     toggleSourceEdit : function(sourceEditMode){
24205         
24206         this.sourceEditMode = sourceEditMode === true;
24207         
24208         if(this.sourceEditMode){
24209  
24210             Roo.get(this.iframe).addClass(['x-hidden','hide']);     //FIXME - what's the BS styles for these
24211             
24212         }else{
24213             Roo.get(this.iframe).removeClass(['x-hidden','hide']);
24214             //this.iframe.className = '';
24215             this.deferFocus();
24216         }
24217         //this.setSize(this.owner.wrap.getSize());
24218         //this.fireEvent('editmodechange', this, this.sourceEditMode);
24219     },
24220
24221     
24222   
24223
24224     /**
24225      * Protected method that will not generally be called directly. If you need/want
24226      * custom HTML cleanup, this is the method you should override.
24227      * @param {String} html The HTML to be cleaned
24228      * return {String} The cleaned HTML
24229      */
24230     cleanHtml : function(html){
24231         html = String(html);
24232         if(html.length > 5){
24233             if(Roo.isSafari){ // strip safari nonsense
24234                 html = html.replace(/\sclass="(?:Apple-style-span|khtml-block-placeholder)"/gi, '');
24235             }
24236         }
24237         if(html == '&nbsp;'){
24238             html = '';
24239         }
24240         return html;
24241     },
24242
24243     /**
24244      * HTML Editor -> Textarea
24245      * Protected method that will not generally be called directly. Syncs the contents
24246      * of the editor iframe with the textarea.
24247      */
24248     syncValue : function(){
24249         if(this.initialized){
24250             var bd = (this.doc.body || this.doc.documentElement);
24251             //this.cleanUpPaste(); -- this is done else where and causes havoc..
24252             var html = bd.innerHTML;
24253             if(Roo.isSafari){
24254                 var bs = bd.getAttribute('style'); // Safari puts text-align styles on the body element!
24255                 var m = bs ? bs.match(/text-align:(.*?);/i) : false;
24256                 if(m && m[1]){
24257                     html = '<div style="'+m[0]+'">' + html + '</div>';
24258                 }
24259             }
24260             html = this.cleanHtml(html);
24261             // fix up the special chars.. normaly like back quotes in word...
24262             // however we do not want to do this with chinese..
24263             html = html.replace(/[\uD800-\uDBFF][\uDC00-\uDFFF]|[\u0080-\uFFFF]/g, function(match) {
24264                 
24265                 var cc = match.charCodeAt();
24266
24267                 // Get the character value, handling surrogate pairs
24268                 if (match.length == 2) {
24269                     // It's a surrogate pair, calculate the Unicode code point
24270                     var high = match.charCodeAt(0) - 0xD800;
24271                     var low  = match.charCodeAt(1) - 0xDC00;
24272                     cc = (high * 0x400) + low + 0x10000;
24273                 }  else if (
24274                     (cc >= 0x4E00 && cc < 0xA000 ) ||
24275                     (cc >= 0x3400 && cc < 0x4E00 ) ||
24276                     (cc >= 0xf900 && cc < 0xfb00 )
24277                 ) {
24278                         return match;
24279                 }  
24280          
24281                 // No, use a numeric entity. Here we brazenly (and possibly mistakenly)
24282                 return "&#" + cc + ";";
24283                 
24284                 
24285             });
24286             
24287             
24288              
24289             if(this.owner.fireEvent('beforesync', this, html) !== false){
24290                 this.el.dom.value = html;
24291                 this.owner.fireEvent('sync', this, html);
24292             }
24293         }
24294     },
24295
24296     /**
24297      * Protected method that will not generally be called directly. Pushes the value of the textarea
24298      * into the iframe editor.
24299      */
24300     pushValue : function(){
24301         if(this.initialized){
24302             var v = this.el.dom.value.trim();
24303             
24304 //            if(v.length < 1){
24305 //                v = '&#160;';
24306 //            }
24307             
24308             if(this.owner.fireEvent('beforepush', this, v) !== false){
24309                 var d = (this.doc.body || this.doc.documentElement);
24310                 d.innerHTML = v;
24311                 this.cleanUpPaste();
24312                 this.el.dom.value = d.innerHTML;
24313                 this.owner.fireEvent('push', this, v);
24314             }
24315         }
24316     },
24317
24318     // private
24319     deferFocus : function(){
24320         this.focus.defer(10, this);
24321     },
24322
24323     // doc'ed in Field
24324     focus : function(){
24325         if(this.win && !this.sourceEditMode){
24326             this.win.focus();
24327         }else{
24328             this.el.focus();
24329         }
24330     },
24331     
24332     assignDocWin: function()
24333     {
24334         var iframe = this.iframe;
24335         
24336          if(Roo.isIE){
24337             this.doc = iframe.contentWindow.document;
24338             this.win = iframe.contentWindow;
24339         } else {
24340 //            if (!Roo.get(this.frameId)) {
24341 //                return;
24342 //            }
24343 //            this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24344 //            this.win = Roo.get(this.frameId).dom.contentWindow;
24345             
24346             if (!Roo.get(this.frameId) && !iframe.contentDocument) {
24347                 return;
24348             }
24349             
24350             this.doc = (iframe.contentDocument || Roo.get(this.frameId).dom.document);
24351             this.win = (iframe.contentWindow || Roo.get(this.frameId).dom.contentWindow);
24352         }
24353     },
24354     
24355     // private
24356     initEditor : function(){
24357         //console.log("INIT EDITOR");
24358         this.assignDocWin();
24359         
24360         
24361         
24362         this.doc.designMode="on";
24363         this.doc.open();
24364         this.doc.write(this.getDocMarkup());
24365         this.doc.close();
24366         
24367         var dbody = (this.doc.body || this.doc.documentElement);
24368         //var ss = this.el.getStyles('font-size', 'font-family', 'background-image', 'background-repeat');
24369         // this copies styles from the containing element into thsi one..
24370         // not sure why we need all of this..
24371         //var ss = this.el.getStyles('font-size', 'background-image', 'background-repeat');
24372         
24373         //var ss = this.el.getStyles( 'background-image', 'background-repeat');
24374         //ss['background-attachment'] = 'fixed'; // w3c
24375         dbody.bgProperties = 'fixed'; // ie
24376         //Roo.DomHelper.applyStyles(dbody, ss);
24377         Roo.EventManager.on(this.doc, {
24378             //'mousedown': this.onEditorEvent,
24379             'mouseup': this.onEditorEvent,
24380             'dblclick': this.onEditorEvent,
24381             'click': this.onEditorEvent,
24382             'keyup': this.onEditorEvent,
24383             buffer:100,
24384             scope: this
24385         });
24386         if(Roo.isGecko){
24387             Roo.EventManager.on(this.doc, 'keypress', this.mozKeyPress, this);
24388         }
24389         if(Roo.isIE || Roo.isSafari || Roo.isOpera){
24390             Roo.EventManager.on(this.doc, 'keydown', this.fixKeys, this);
24391         }
24392         this.initialized = true;
24393
24394         this.owner.fireEvent('initialize', this);
24395         this.pushValue();
24396     },
24397
24398     // private
24399     onDestroy : function(){
24400         
24401         
24402         
24403         if(this.rendered){
24404             
24405             //for (var i =0; i < this.toolbars.length;i++) {
24406             //    // fixme - ask toolbars for heights?
24407             //    this.toolbars[i].onDestroy();
24408            // }
24409             
24410             //this.wrap.dom.innerHTML = '';
24411             //this.wrap.remove();
24412         }
24413     },
24414
24415     // private
24416     onFirstFocus : function(){
24417         
24418         this.assignDocWin();
24419         
24420         
24421         this.activated = true;
24422          
24423     
24424         if(Roo.isGecko){ // prevent silly gecko errors
24425             this.win.focus();
24426             var s = this.win.getSelection();
24427             if(!s.focusNode || s.focusNode.nodeType != 3){
24428                 var r = s.getRangeAt(0);
24429                 r.selectNodeContents((this.doc.body || this.doc.documentElement));
24430                 r.collapse(true);
24431                 this.deferFocus();
24432             }
24433             try{
24434                 this.execCmd('useCSS', true);
24435                 this.execCmd('styleWithCSS', false);
24436             }catch(e){}
24437         }
24438         this.owner.fireEvent('activate', this);
24439     },
24440
24441     // private
24442     adjustFont: function(btn){
24443         var adjust = btn.cmd == 'increasefontsize' ? 1 : -1;
24444         //if(Roo.isSafari){ // safari
24445         //    adjust *= 2;
24446        // }
24447         var v = parseInt(this.doc.queryCommandValue('FontSize')|| 3, 10);
24448         if(Roo.isSafari){ // safari
24449             var sm = { 10 : 1, 13: 2, 16:3, 18:4, 24: 5, 32:6, 48: 7 };
24450             v =  (v < 10) ? 10 : v;
24451             v =  (v > 48) ? 48 : v;
24452             v = typeof(sm[v]) == 'undefined' ? 1 : sm[v];
24453             
24454         }
24455         
24456         
24457         v = Math.max(1, v+adjust);
24458         
24459         this.execCmd('FontSize', v  );
24460     },
24461
24462     onEditorEvent : function(e)
24463     {
24464         this.owner.fireEvent('editorevent', this, e);
24465       //  this.updateToolbar();
24466         this.syncValue(); //we can not sync so often.. sync cleans, so this breaks stuff
24467     },
24468
24469     insertTag : function(tg)
24470     {
24471         // could be a bit smarter... -> wrap the current selected tRoo..
24472         if (tg.toLowerCase() == 'span' ||
24473             tg.toLowerCase() == 'code' ||
24474             tg.toLowerCase() == 'sup' ||
24475             tg.toLowerCase() == 'sub' 
24476             ) {
24477             
24478             range = this.createRange(this.getSelection());
24479             var wrappingNode = this.doc.createElement(tg.toLowerCase());
24480             wrappingNode.appendChild(range.extractContents());
24481             range.insertNode(wrappingNode);
24482
24483             return;
24484             
24485             
24486             
24487         }
24488         this.execCmd("formatblock",   tg);
24489         
24490     },
24491     
24492     insertText : function(txt)
24493     {
24494         
24495         
24496         var range = this.createRange();
24497         range.deleteContents();
24498                //alert(Sender.getAttribute('label'));
24499                
24500         range.insertNode(this.doc.createTextNode(txt));
24501     } ,
24502     
24503      
24504
24505     /**
24506      * Executes a Midas editor command on the editor document and performs necessary focus and
24507      * toolbar updates. <b>This should only be called after the editor is initialized.</b>
24508      * @param {String} cmd The Midas command
24509      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24510      */
24511     relayCmd : function(cmd, value){
24512         this.win.focus();
24513         this.execCmd(cmd, value);
24514         this.owner.fireEvent('editorevent', this);
24515         //this.updateToolbar();
24516         this.owner.deferFocus();
24517     },
24518
24519     /**
24520      * Executes a Midas editor command directly on the editor document.
24521      * For visual commands, you should use {@link #relayCmd} instead.
24522      * <b>This should only be called after the editor is initialized.</b>
24523      * @param {String} cmd The Midas command
24524      * @param {String/Boolean} value (optional) The value to pass to the command (defaults to null)
24525      */
24526     execCmd : function(cmd, value){
24527         this.doc.execCommand(cmd, false, value === undefined ? null : value);
24528         this.syncValue();
24529     },
24530  
24531  
24532    
24533     /**
24534      * Inserts the passed text at the current cursor position. Note: the editor must be initialized and activated
24535      * to insert tRoo.
24536      * @param {String} text | dom node.. 
24537      */
24538     insertAtCursor : function(text)
24539     {
24540         
24541         if(!this.activated){
24542             return;
24543         }
24544         /*
24545         if(Roo.isIE){
24546             this.win.focus();
24547             var r = this.doc.selection.createRange();
24548             if(r){
24549                 r.collapse(true);
24550                 r.pasteHTML(text);
24551                 this.syncValue();
24552                 this.deferFocus();
24553             
24554             }
24555             return;
24556         }
24557         */
24558         if(Roo.isGecko || Roo.isOpera || Roo.isSafari){
24559             this.win.focus();
24560             
24561             
24562             // from jquery ui (MIT licenced)
24563             var range, node;
24564             var win = this.win;
24565             
24566             if (win.getSelection && win.getSelection().getRangeAt) {
24567                 range = win.getSelection().getRangeAt(0);
24568                 node = typeof(text) == 'string' ? range.createContextualFragment(text) : text;
24569                 range.insertNode(node);
24570             } else if (win.document.selection && win.document.selection.createRange) {
24571                 // no firefox support
24572                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24573                 win.document.selection.createRange().pasteHTML(txt);
24574             } else {
24575                 // no firefox support
24576                 var txt = typeof(text) == 'string' ? text : text.outerHTML;
24577                 this.execCmd('InsertHTML', txt);
24578             } 
24579             
24580             this.syncValue();
24581             
24582             this.deferFocus();
24583         }
24584     },
24585  // private
24586     mozKeyPress : function(e){
24587         if(e.ctrlKey){
24588             var c = e.getCharCode(), cmd;
24589           
24590             if(c > 0){
24591                 c = String.fromCharCode(c).toLowerCase();
24592                 switch(c){
24593                     case 'b':
24594                         cmd = 'bold';
24595                         break;
24596                     case 'i':
24597                         cmd = 'italic';
24598                         break;
24599                     
24600                     case 'u':
24601                         cmd = 'underline';
24602                         break;
24603                     
24604                     case 'v':
24605                         this.cleanUpPaste.defer(100, this);
24606                         return;
24607                         
24608                 }
24609                 if(cmd){
24610                     this.win.focus();
24611                     this.execCmd(cmd);
24612                     this.deferFocus();
24613                     e.preventDefault();
24614                 }
24615                 
24616             }
24617         }
24618     },
24619
24620     // private
24621     fixKeys : function(){ // load time branching for fastest keydown performance
24622         if(Roo.isIE){
24623             return function(e){
24624                 var k = e.getKey(), r;
24625                 if(k == e.TAB){
24626                     e.stopEvent();
24627                     r = this.doc.selection.createRange();
24628                     if(r){
24629                         r.collapse(true);
24630                         r.pasteHTML('&#160;&#160;&#160;&#160;');
24631                         this.deferFocus();
24632                     }
24633                     return;
24634                 }
24635                 
24636                 if(k == e.ENTER){
24637                     r = this.doc.selection.createRange();
24638                     if(r){
24639                         var target = r.parentElement();
24640                         if(!target || target.tagName.toLowerCase() != 'li'){
24641                             e.stopEvent();
24642                             r.pasteHTML('<br />');
24643                             r.collapse(false);
24644                             r.select();
24645                         }
24646                     }
24647                 }
24648                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24649                     this.cleanUpPaste.defer(100, this);
24650                     return;
24651                 }
24652                 
24653                 
24654             };
24655         }else if(Roo.isOpera){
24656             return function(e){
24657                 var k = e.getKey();
24658                 if(k == e.TAB){
24659                     e.stopEvent();
24660                     this.win.focus();
24661                     this.execCmd('InsertHTML','&#160;&#160;&#160;&#160;');
24662                     this.deferFocus();
24663                 }
24664                 if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24665                     this.cleanUpPaste.defer(100, this);
24666                     return;
24667                 }
24668                 
24669             };
24670         }else if(Roo.isSafari){
24671             return function(e){
24672                 var k = e.getKey();
24673                 
24674                 if(k == e.TAB){
24675                     e.stopEvent();
24676                     this.execCmd('InsertText','\t');
24677                     this.deferFocus();
24678                     return;
24679                 }
24680                if (String.fromCharCode(k).toLowerCase() == 'v') { // paste
24681                     this.cleanUpPaste.defer(100, this);
24682                     return;
24683                 }
24684                 
24685              };
24686         }
24687     }(),
24688     
24689     getAllAncestors: function()
24690     {
24691         var p = this.getSelectedNode();
24692         var a = [];
24693         if (!p) {
24694             a.push(p); // push blank onto stack..
24695             p = this.getParentElement();
24696         }
24697         
24698         
24699         while (p && (p.nodeType == 1) && (p.tagName.toLowerCase() != 'body')) {
24700             a.push(p);
24701             p = p.parentNode;
24702         }
24703         a.push(this.doc.body);
24704         return a;
24705     },
24706     lastSel : false,
24707     lastSelNode : false,
24708     
24709     
24710     getSelection : function() 
24711     {
24712         this.assignDocWin();
24713         return Roo.isIE ? this.doc.selection : this.win.getSelection();
24714     },
24715     
24716     getSelectedNode: function() 
24717     {
24718         // this may only work on Gecko!!!
24719         
24720         // should we cache this!!!!
24721         
24722         
24723         
24724          
24725         var range = this.createRange(this.getSelection()).cloneRange();
24726         
24727         if (Roo.isIE) {
24728             var parent = range.parentElement();
24729             while (true) {
24730                 var testRange = range.duplicate();
24731                 testRange.moveToElementText(parent);
24732                 if (testRange.inRange(range)) {
24733                     break;
24734                 }
24735                 if ((parent.nodeType != 1) || (parent.tagName.toLowerCase() == 'body')) {
24736                     break;
24737                 }
24738                 parent = parent.parentElement;
24739             }
24740             return parent;
24741         }
24742         
24743         // is ancestor a text element.
24744         var ac =  range.commonAncestorContainer;
24745         if (ac.nodeType == 3) {
24746             ac = ac.parentNode;
24747         }
24748         
24749         var ar = ac.childNodes;
24750          
24751         var nodes = [];
24752         var other_nodes = [];
24753         var has_other_nodes = false;
24754         for (var i=0;i<ar.length;i++) {
24755             if ((ar[i].nodeType == 3) && (!ar[i].data.length)) { // empty text ? 
24756                 continue;
24757             }
24758             // fullly contained node.
24759             
24760             if (this.rangeIntersectsNode(range,ar[i]) && this.rangeCompareNode(range,ar[i]) == 3) {
24761                 nodes.push(ar[i]);
24762                 continue;
24763             }
24764             
24765             // probably selected..
24766             if ((ar[i].nodeType == 1) && this.rangeIntersectsNode(range,ar[i]) && (this.rangeCompareNode(range,ar[i]) > 0)) {
24767                 other_nodes.push(ar[i]);
24768                 continue;
24769             }
24770             // outer..
24771             if (!this.rangeIntersectsNode(range,ar[i])|| (this.rangeCompareNode(range,ar[i]) == 0))  {
24772                 continue;
24773             }
24774             
24775             
24776             has_other_nodes = true;
24777         }
24778         if (!nodes.length && other_nodes.length) {
24779             nodes= other_nodes;
24780         }
24781         if (has_other_nodes || !nodes.length || (nodes.length > 1)) {
24782             return false;
24783         }
24784         
24785         return nodes[0];
24786     },
24787     createRange: function(sel)
24788     {
24789         // this has strange effects when using with 
24790         // top toolbar - not sure if it's a great idea.
24791         //this.editor.contentWindow.focus();
24792         if (typeof sel != "undefined") {
24793             try {
24794                 return sel.getRangeAt ? sel.getRangeAt(0) : sel.createRange();
24795             } catch(e) {
24796                 return this.doc.createRange();
24797             }
24798         } else {
24799             return this.doc.createRange();
24800         }
24801     },
24802     getParentElement: function()
24803     {
24804         
24805         this.assignDocWin();
24806         var sel = Roo.isIE ? this.doc.selection : this.win.getSelection();
24807         
24808         var range = this.createRange(sel);
24809          
24810         try {
24811             var p = range.commonAncestorContainer;
24812             while (p.nodeType == 3) { // text node
24813                 p = p.parentNode;
24814             }
24815             return p;
24816         } catch (e) {
24817             return null;
24818         }
24819     
24820     },
24821     /***
24822      *
24823      * Range intersection.. the hard stuff...
24824      *  '-1' = before
24825      *  '0' = hits..
24826      *  '1' = after.
24827      *         [ -- selected range --- ]
24828      *   [fail]                        [fail]
24829      *
24830      *    basically..
24831      *      if end is before start or  hits it. fail.
24832      *      if start is after end or hits it fail.
24833      *
24834      *   if either hits (but other is outside. - then it's not 
24835      *   
24836      *    
24837      **/
24838     
24839     
24840     // @see http://www.thismuchiknow.co.uk/?p=64.
24841     rangeIntersectsNode : function(range, node)
24842     {
24843         var nodeRange = node.ownerDocument.createRange();
24844         try {
24845             nodeRange.selectNode(node);
24846         } catch (e) {
24847             nodeRange.selectNodeContents(node);
24848         }
24849     
24850         var rangeStartRange = range.cloneRange();
24851         rangeStartRange.collapse(true);
24852     
24853         var rangeEndRange = range.cloneRange();
24854         rangeEndRange.collapse(false);
24855     
24856         var nodeStartRange = nodeRange.cloneRange();
24857         nodeStartRange.collapse(true);
24858     
24859         var nodeEndRange = nodeRange.cloneRange();
24860         nodeEndRange.collapse(false);
24861     
24862         return rangeStartRange.compareBoundaryPoints(
24863                  Range.START_TO_START, nodeEndRange) == -1 &&
24864                rangeEndRange.compareBoundaryPoints(
24865                  Range.START_TO_START, nodeStartRange) == 1;
24866         
24867          
24868     },
24869     rangeCompareNode : function(range, node)
24870     {
24871         var nodeRange = node.ownerDocument.createRange();
24872         try {
24873             nodeRange.selectNode(node);
24874         } catch (e) {
24875             nodeRange.selectNodeContents(node);
24876         }
24877         
24878         
24879         range.collapse(true);
24880     
24881         nodeRange.collapse(true);
24882      
24883         var ss = range.compareBoundaryPoints( Range.START_TO_START, nodeRange);
24884         var ee = range.compareBoundaryPoints(  Range.END_TO_END, nodeRange);
24885          
24886         //Roo.log(node.tagName + ': ss='+ss +', ee='+ee)
24887         
24888         var nodeIsBefore   =  ss == 1;
24889         var nodeIsAfter    = ee == -1;
24890         
24891         if (nodeIsBefore && nodeIsAfter) {
24892             return 0; // outer
24893         }
24894         if (!nodeIsBefore && nodeIsAfter) {
24895             return 1; //right trailed.
24896         }
24897         
24898         if (nodeIsBefore && !nodeIsAfter) {
24899             return 2;  // left trailed.
24900         }
24901         // fully contined.
24902         return 3;
24903     },
24904
24905     // private? - in a new class?
24906     cleanUpPaste :  function()
24907     {
24908         // cleans up the whole document..
24909         Roo.log('cleanuppaste');
24910         
24911         this.cleanUpChildren(this.doc.body);
24912         var clean = this.cleanWordChars(this.doc.body.innerHTML);
24913         if (clean != this.doc.body.innerHTML) {
24914             this.doc.body.innerHTML = clean;
24915         }
24916         
24917     },
24918     
24919     cleanWordChars : function(input) {// change the chars to hex code
24920         var he = Roo.HtmlEditorCore;
24921         
24922         var output = input;
24923         Roo.each(he.swapCodes, function(sw) { 
24924             var swapper = new RegExp("\\u" + sw[0].toString(16), "g"); // hex codes
24925             
24926             output = output.replace(swapper, sw[1]);
24927         });
24928         
24929         return output;
24930     },
24931     
24932     
24933     cleanUpChildren : function (n)
24934     {
24935         if (!n.childNodes.length) {
24936             return;
24937         }
24938         for (var i = n.childNodes.length-1; i > -1 ; i--) {
24939            this.cleanUpChild(n.childNodes[i]);
24940         }
24941     },
24942     
24943     
24944         
24945     
24946     cleanUpChild : function (node)
24947     {
24948         var ed = this;
24949         //console.log(node);
24950         if (node.nodeName == "#text") {
24951             // clean up silly Windows -- stuff?
24952             return; 
24953         }
24954         if (node.nodeName == "#comment") {
24955             node.parentNode.removeChild(node);
24956             // clean up silly Windows -- stuff?
24957             return; 
24958         }
24959         var lcname = node.tagName.toLowerCase();
24960         // we ignore whitelists... ?? = not really the way to go, but we probably have not got a full
24961         // whitelist of tags..
24962         
24963         if (this.black.indexOf(lcname) > -1 && this.clearUp ) {
24964             // remove node.
24965             node.parentNode.removeChild(node);
24966             return;
24967             
24968         }
24969         
24970         var remove_keep_children= Roo.HtmlEditorCore.remove.indexOf(node.tagName.toLowerCase()) > -1;
24971         
24972         // spans with no attributes - just remove them..
24973         if ((!node.attributes || !node.attributes.length) && lcname == 'span') { 
24974             remove_keep_children = true;
24975         }
24976         
24977         // remove <a name=....> as rendering on yahoo mailer is borked with this.
24978         // this will have to be flaged elsewhere - perhaps ablack=name... on the mailer..
24979         
24980         //if (node.tagName.toLowerCase() == 'a' && !node.hasAttribute('href')) {
24981         //    remove_keep_children = true;
24982         //}
24983         
24984         if (remove_keep_children) {
24985             this.cleanUpChildren(node);
24986             // inserts everything just before this node...
24987             while (node.childNodes.length) {
24988                 var cn = node.childNodes[0];
24989                 node.removeChild(cn);
24990                 node.parentNode.insertBefore(cn, node);
24991             }
24992             node.parentNode.removeChild(node);
24993             return;
24994         }
24995         
24996         if (!node.attributes || !node.attributes.length) {
24997             
24998           
24999             
25000             
25001             this.cleanUpChildren(node);
25002             return;
25003         }
25004         
25005         function cleanAttr(n,v)
25006         {
25007             
25008             if (v.match(/^\./) || v.match(/^\//)) {
25009                 return;
25010             }
25011             if (v.match(/^(http|https):\/\//) || v.match(/^mailto:/) || v.match(/^ftp:/)) {
25012                 return;
25013             }
25014             if (v.match(/^#/)) {
25015                 return;
25016             }
25017             if (v.match(/^\{/)) { // allow template editing.
25018                 return;
25019             }
25020 //            Roo.log("(REMOVE TAG)"+ node.tagName +'.' + n + '=' + v);
25021             node.removeAttribute(n);
25022             
25023         }
25024         
25025         var cwhite = this.cwhite;
25026         var cblack = this.cblack;
25027             
25028         function cleanStyle(n,v)
25029         {
25030             if (v.match(/expression/)) { //XSS?? should we even bother..
25031                 node.removeAttribute(n);
25032                 return;
25033             }
25034             
25035             var parts = v.split(/;/);
25036             var clean = [];
25037             
25038             Roo.each(parts, function(p) {
25039                 p = p.replace(/^\s+/g,'').replace(/\s+$/g,'');
25040                 if (!p.length) {
25041                     return true;
25042                 }
25043                 var l = p.split(':').shift().replace(/\s+/g,'');
25044                 l = l.replace(/^\s+/g,'').replace(/\s+$/g,'');
25045                 
25046                 if ( cwhite.length && cblack.indexOf(l) > -1) {
25047 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25048                     //node.removeAttribute(n);
25049                     return true;
25050                 }
25051                 //Roo.log()
25052                 // only allow 'c whitelisted system attributes'
25053                 if ( cwhite.length &&  cwhite.indexOf(l) < 0) {
25054 //                    Roo.log('(REMOVE CSS)' + node.tagName +'.' + n + ':'+l + '=' + v);
25055                     //node.removeAttribute(n);
25056                     return true;
25057                 }
25058                 
25059                 
25060                  
25061                 
25062                 clean.push(p);
25063                 return true;
25064             });
25065             if (clean.length) { 
25066                 node.setAttribute(n, clean.join(';'));
25067             } else {
25068                 node.removeAttribute(n);
25069             }
25070             
25071         }
25072         
25073         
25074         for (var i = node.attributes.length-1; i > -1 ; i--) {
25075             var a = node.attributes[i];
25076             //console.log(a);
25077             
25078             if (a.name.toLowerCase().substr(0,2)=='on')  {
25079                 node.removeAttribute(a.name);
25080                 continue;
25081             }
25082             if (Roo.HtmlEditorCore.ablack.indexOf(a.name.toLowerCase()) > -1) {
25083                 node.removeAttribute(a.name);
25084                 continue;
25085             }
25086             if (Roo.HtmlEditorCore.aclean.indexOf(a.name.toLowerCase()) > -1) {
25087                 cleanAttr(a.name,a.value); // fixme..
25088                 continue;
25089             }
25090             if (a.name == 'style') {
25091                 cleanStyle(a.name,a.value);
25092                 continue;
25093             }
25094             /// clean up MS crap..
25095             // tecnically this should be a list of valid class'es..
25096             
25097             
25098             if (a.name == 'class') {
25099                 if (a.value.match(/^Mso/)) {
25100                     node.removeAttribute('class');
25101                 }
25102                 
25103                 if (a.value.match(/^body$/)) {
25104                     node.removeAttribute('class');
25105                 }
25106                 continue;
25107             }
25108             
25109             // style cleanup!?
25110             // class cleanup?
25111             
25112         }
25113         
25114         
25115         this.cleanUpChildren(node);
25116         
25117         
25118     },
25119     
25120     /**
25121      * Clean up MS wordisms...
25122      */
25123     cleanWord : function(node)
25124     {
25125         if (!node) {
25126             this.cleanWord(this.doc.body);
25127             return;
25128         }
25129         
25130         if(
25131                 node.nodeName == 'SPAN' &&
25132                 !node.hasAttributes() &&
25133                 node.childNodes.length == 1 &&
25134                 node.firstChild.nodeName == "#text"  
25135         ) {
25136             var textNode = node.firstChild;
25137             node.removeChild(textNode);
25138             if (node.getAttribute('lang') != 'zh-CN') {   // do not space pad on chinese characters..
25139                 node.parentNode.insertBefore(node.ownerDocument.createTextNode(" "), node);
25140             }
25141             node.parentNode.insertBefore(textNode, node);
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.removeChild(node);
25146         }
25147         
25148         if (node.nodeName == "#text") {
25149             // clean up silly Windows -- stuff?
25150             return; 
25151         }
25152         if (node.nodeName == "#comment") {
25153             node.parentNode.removeChild(node);
25154             // clean up silly Windows -- stuff?
25155             return; 
25156         }
25157         
25158         if (node.tagName.toLowerCase().match(/^(style|script|applet|embed|noframes|noscript)$/)) {
25159             node.parentNode.removeChild(node);
25160             return;
25161         }
25162         //Roo.log(node.tagName);
25163         // remove - but keep children..
25164         if (node.tagName.toLowerCase().match(/^(meta|link|\\?xml:|st1:|o:|v:|font)/)) {
25165             //Roo.log('-- removed');
25166             while (node.childNodes.length) {
25167                 var cn = node.childNodes[0];
25168                 node.removeChild(cn);
25169                 node.parentNode.insertBefore(cn, node);
25170                 // move node to parent - and clean it..
25171                 this.cleanWord(cn);
25172             }
25173             node.parentNode.removeChild(node);
25174             /// no need to iterate chidlren = it's got none..
25175             //this.iterateChildren(node, this.cleanWord);
25176             return;
25177         }
25178         // clean styles
25179         if (node.className.length) {
25180             
25181             var cn = node.className.split(/\W+/);
25182             var cna = [];
25183             Roo.each(cn, function(cls) {
25184                 if (cls.match(/Mso[a-zA-Z]+/)) {
25185                     return;
25186                 }
25187                 cna.push(cls);
25188             });
25189             node.className = cna.length ? cna.join(' ') : '';
25190             if (!cna.length) {
25191                 node.removeAttribute("class");
25192             }
25193         }
25194         
25195         if (node.hasAttribute("lang")) {
25196             node.removeAttribute("lang");
25197         }
25198         
25199         if (node.hasAttribute("style")) {
25200             
25201             var styles = node.getAttribute("style").split(";");
25202             var nstyle = [];
25203             Roo.each(styles, function(s) {
25204                 if (!s.match(/:/)) {
25205                     return;
25206                 }
25207                 var kv = s.split(":");
25208                 if (kv[0].match(/^(mso-|line|font|background|margin|padding|color)/)) {
25209                     return;
25210                 }
25211                 // what ever is left... we allow.
25212                 nstyle.push(s);
25213             });
25214             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25215             if (!nstyle.length) {
25216                 node.removeAttribute('style');
25217             }
25218         }
25219         this.iterateChildren(node, this.cleanWord);
25220         
25221         
25222         
25223     },
25224     /**
25225      * iterateChildren of a Node, calling fn each time, using this as the scole..
25226      * @param {DomNode} node node to iterate children of.
25227      * @param {Function} fn method of this class to call on each item.
25228      */
25229     iterateChildren : function(node, fn)
25230     {
25231         if (!node.childNodes.length) {
25232                 return;
25233         }
25234         for (var i = node.childNodes.length-1; i > -1 ; i--) {
25235            fn.call(this, node.childNodes[i])
25236         }
25237     },
25238     
25239     
25240     /**
25241      * cleanTableWidths.
25242      *
25243      * Quite often pasting from word etc.. results in tables with column and widths.
25244      * This does not work well on fluid HTML layouts - like emails. - so this code should hunt an destroy them..
25245      *
25246      */
25247     cleanTableWidths : function(node)
25248     {
25249          
25250          
25251         if (!node) {
25252             this.cleanTableWidths(this.doc.body);
25253             return;
25254         }
25255         
25256         // ignore list...
25257         if (node.nodeName == "#text" || node.nodeName == "#comment") {
25258             return; 
25259         }
25260         Roo.log(node.tagName);
25261         if (!node.tagName.toLowerCase().match(/^(table|td|tr)$/)) {
25262             this.iterateChildren(node, this.cleanTableWidths);
25263             return;
25264         }
25265         if (node.hasAttribute('width')) {
25266             node.removeAttribute('width');
25267         }
25268         
25269          
25270         if (node.hasAttribute("style")) {
25271             // pretty basic...
25272             
25273             var styles = node.getAttribute("style").split(";");
25274             var nstyle = [];
25275             Roo.each(styles, function(s) {
25276                 if (!s.match(/:/)) {
25277                     return;
25278                 }
25279                 var kv = s.split(":");
25280                 if (kv[0].match(/^\s*(width|min-width)\s*$/)) {
25281                     return;
25282                 }
25283                 // what ever is left... we allow.
25284                 nstyle.push(s);
25285             });
25286             node.setAttribute("style", nstyle.length ? nstyle.join(';') : '');
25287             if (!nstyle.length) {
25288                 node.removeAttribute('style');
25289             }
25290         }
25291         
25292         this.iterateChildren(node, this.cleanTableWidths);
25293         
25294         
25295     },
25296     
25297     
25298     
25299     
25300     domToHTML : function(currentElement, depth, nopadtext) {
25301         
25302         depth = depth || 0;
25303         nopadtext = nopadtext || false;
25304     
25305         if (!currentElement) {
25306             return this.domToHTML(this.doc.body);
25307         }
25308         
25309         //Roo.log(currentElement);
25310         var j;
25311         var allText = false;
25312         var nodeName = currentElement.nodeName;
25313         var tagName = Roo.util.Format.htmlEncode(currentElement.tagName);
25314         
25315         if  (nodeName == '#text') {
25316             
25317             return nopadtext ? currentElement.nodeValue : currentElement.nodeValue.trim();
25318         }
25319         
25320         
25321         var ret = '';
25322         if (nodeName != 'BODY') {
25323              
25324             var i = 0;
25325             // Prints the node tagName, such as <A>, <IMG>, etc
25326             if (tagName) {
25327                 var attr = [];
25328                 for(i = 0; i < currentElement.attributes.length;i++) {
25329                     // quoting?
25330                     var aname = currentElement.attributes.item(i).name;
25331                     if (!currentElement.attributes.item(i).value.length) {
25332                         continue;
25333                     }
25334                     attr.push(aname + '="' + Roo.util.Format.htmlEncode(currentElement.attributes.item(i).value) + '"' );
25335                 }
25336                 
25337                 ret = "<"+currentElement.tagName+ ( attr.length ? (' ' + attr.join(' ') ) : '') + ">";
25338             } 
25339             else {
25340                 
25341                 // eack
25342             }
25343         } else {
25344             tagName = false;
25345         }
25346         if (['IMG', 'BR', 'HR', 'INPUT'].indexOf(tagName) > -1) {
25347             return ret;
25348         }
25349         if (['PRE', 'TEXTAREA', 'TD', 'A', 'SPAN'].indexOf(tagName) > -1) { // or code?
25350             nopadtext = true;
25351         }
25352         
25353         
25354         // Traverse the tree
25355         i = 0;
25356         var currentElementChild = currentElement.childNodes.item(i);
25357         var allText = true;
25358         var innerHTML  = '';
25359         lastnode = '';
25360         while (currentElementChild) {
25361             // Formatting code (indent the tree so it looks nice on the screen)
25362             var nopad = nopadtext;
25363             if (lastnode == 'SPAN') {
25364                 nopad  = true;
25365             }
25366             // text
25367             if  (currentElementChild.nodeName == '#text') {
25368                 var toadd = Roo.util.Format.htmlEncode(currentElementChild.nodeValue);
25369                 toadd = nopadtext ? toadd : toadd.trim();
25370                 if (!nopad && toadd.length > 80) {
25371                     innerHTML  += "\n" + (new Array( depth + 1 )).join( "  "  );
25372                 }
25373                 innerHTML  += toadd;
25374                 
25375                 i++;
25376                 currentElementChild = currentElement.childNodes.item(i);
25377                 lastNode = '';
25378                 continue;
25379             }
25380             allText = false;
25381             
25382             innerHTML  += nopad ? '' : "\n" + (new Array( depth + 1 )).join( "  "  );
25383                 
25384             // Recursively traverse the tree structure of the child node
25385             innerHTML   += this.domToHTML(currentElementChild, depth+1, nopadtext);
25386             lastnode = currentElementChild.nodeName;
25387             i++;
25388             currentElementChild=currentElement.childNodes.item(i);
25389         }
25390         
25391         ret += innerHTML;
25392         
25393         if (!allText) {
25394                 // The remaining code is mostly for formatting the tree
25395             ret+= nopadtext ? '' : "\n" + (new Array( depth  )).join( "  "  );
25396         }
25397         
25398         
25399         if (tagName) {
25400             ret+= "</"+tagName+">";
25401         }
25402         return ret;
25403         
25404     },
25405         
25406     applyBlacklists : function()
25407     {
25408         var w = typeof(this.owner.white) != 'undefined' && this.owner.white ? this.owner.white  : [];
25409         var b = typeof(this.owner.black) != 'undefined' && this.owner.black ? this.owner.black :  [];
25410         
25411         this.white = [];
25412         this.black = [];
25413         Roo.each(Roo.HtmlEditorCore.white, function(tag) {
25414             if (b.indexOf(tag) > -1) {
25415                 return;
25416             }
25417             this.white.push(tag);
25418             
25419         }, this);
25420         
25421         Roo.each(w, function(tag) {
25422             if (b.indexOf(tag) > -1) {
25423                 return;
25424             }
25425             if (this.white.indexOf(tag) > -1) {
25426                 return;
25427             }
25428             this.white.push(tag);
25429             
25430         }, this);
25431         
25432         
25433         Roo.each(Roo.HtmlEditorCore.black, function(tag) {
25434             if (w.indexOf(tag) > -1) {
25435                 return;
25436             }
25437             this.black.push(tag);
25438             
25439         }, this);
25440         
25441         Roo.each(b, function(tag) {
25442             if (w.indexOf(tag) > -1) {
25443                 return;
25444             }
25445             if (this.black.indexOf(tag) > -1) {
25446                 return;
25447             }
25448             this.black.push(tag);
25449             
25450         }, this);
25451         
25452         
25453         w = typeof(this.owner.cwhite) != 'undefined' && this.owner.cwhite ? this.owner.cwhite  : [];
25454         b = typeof(this.owner.cblack) != 'undefined' && this.owner.cblack ? this.owner.cblack :  [];
25455         
25456         this.cwhite = [];
25457         this.cblack = [];
25458         Roo.each(Roo.HtmlEditorCore.cwhite, function(tag) {
25459             if (b.indexOf(tag) > -1) {
25460                 return;
25461             }
25462             this.cwhite.push(tag);
25463             
25464         }, this);
25465         
25466         Roo.each(w, function(tag) {
25467             if (b.indexOf(tag) > -1) {
25468                 return;
25469             }
25470             if (this.cwhite.indexOf(tag) > -1) {
25471                 return;
25472             }
25473             this.cwhite.push(tag);
25474             
25475         }, this);
25476         
25477         
25478         Roo.each(Roo.HtmlEditorCore.cblack, function(tag) {
25479             if (w.indexOf(tag) > -1) {
25480                 return;
25481             }
25482             this.cblack.push(tag);
25483             
25484         }, this);
25485         
25486         Roo.each(b, function(tag) {
25487             if (w.indexOf(tag) > -1) {
25488                 return;
25489             }
25490             if (this.cblack.indexOf(tag) > -1) {
25491                 return;
25492             }
25493             this.cblack.push(tag);
25494             
25495         }, this);
25496     },
25497     
25498     setStylesheets : function(stylesheets)
25499     {
25500         if(typeof(stylesheets) == 'string'){
25501             Roo.get(this.iframe.contentDocument.head).createChild({
25502                 tag : 'link',
25503                 rel : 'stylesheet',
25504                 type : 'text/css',
25505                 href : stylesheets
25506             });
25507             
25508             return;
25509         }
25510         var _this = this;
25511      
25512         Roo.each(stylesheets, function(s) {
25513             if(!s.length){
25514                 return;
25515             }
25516             
25517             Roo.get(_this.iframe.contentDocument.head).createChild({
25518                 tag : 'link',
25519                 rel : 'stylesheet',
25520                 type : 'text/css',
25521                 href : s
25522             });
25523         });
25524
25525         
25526     },
25527     
25528     removeStylesheets : function()
25529     {
25530         var _this = this;
25531         
25532         Roo.each(Roo.get(_this.iframe.contentDocument.head).select('link[rel=stylesheet]', true).elements, function(s){
25533             s.remove();
25534         });
25535     },
25536     
25537     setStyle : function(style)
25538     {
25539         Roo.get(this.iframe.contentDocument.head).createChild({
25540             tag : 'style',
25541             type : 'text/css',
25542             html : style
25543         });
25544
25545         return;
25546     }
25547     
25548     // hide stuff that is not compatible
25549     /**
25550      * @event blur
25551      * @hide
25552      */
25553     /**
25554      * @event change
25555      * @hide
25556      */
25557     /**
25558      * @event focus
25559      * @hide
25560      */
25561     /**
25562      * @event specialkey
25563      * @hide
25564      */
25565     /**
25566      * @cfg {String} fieldClass @hide
25567      */
25568     /**
25569      * @cfg {String} focusClass @hide
25570      */
25571     /**
25572      * @cfg {String} autoCreate @hide
25573      */
25574     /**
25575      * @cfg {String} inputType @hide
25576      */
25577     /**
25578      * @cfg {String} invalidClass @hide
25579      */
25580     /**
25581      * @cfg {String} invalidText @hide
25582      */
25583     /**
25584      * @cfg {String} msgFx @hide
25585      */
25586     /**
25587      * @cfg {String} validateOnBlur @hide
25588      */
25589 });
25590
25591 Roo.HtmlEditorCore.white = [
25592         'area', 'br', 'img', 'input', 'hr', 'wbr',
25593         
25594        'address', 'blockquote', 'center', 'dd',      'dir',       'div', 
25595        'dl',      'dt',         'h1',     'h2',      'h3',        'h4', 
25596        'h5',      'h6',         'hr',     'isindex', 'listing',   'marquee', 
25597        'menu',    'multicol',   'ol',     'p',       'plaintext', 'pre', 
25598        'table',   'ul',         'xmp', 
25599        
25600        'caption', 'col', 'colgroup', 'tbody', 'td', 'tfoot', 'th', 
25601       'thead',   'tr', 
25602      
25603       'dir', 'menu', 'ol', 'ul', 'dl',
25604        
25605       'embed',  'object'
25606 ];
25607
25608
25609 Roo.HtmlEditorCore.black = [
25610     //    'embed',  'object', // enable - backend responsiblity to clean thiese
25611         'applet', // 
25612         'base',   'basefont', 'bgsound', 'blink',  'body', 
25613         'frame',  'frameset', 'head',    'html',   'ilayer', 
25614         'iframe', 'layer',  'link',     'meta',    'object',   
25615         'script', 'style' ,'title',  'xml' // clean later..
25616 ];
25617 Roo.HtmlEditorCore.clean = [
25618     'script', 'style', 'title', 'xml'
25619 ];
25620 Roo.HtmlEditorCore.remove = [
25621     'font'
25622 ];
25623 // attributes..
25624
25625 Roo.HtmlEditorCore.ablack = [
25626     'on'
25627 ];
25628     
25629 Roo.HtmlEditorCore.aclean = [ 
25630     'action', 'background', 'codebase', 'dynsrc', 'href', 'lowsrc' 
25631 ];
25632
25633 // protocols..
25634 Roo.HtmlEditorCore.pwhite= [
25635         'http',  'https',  'mailto'
25636 ];
25637
25638 // white listed style attributes.
25639 Roo.HtmlEditorCore.cwhite= [
25640       //  'text-align', /// default is to allow most things..
25641       
25642          
25643 //        'font-size'//??
25644 ];
25645
25646 // black listed style attributes.
25647 Roo.HtmlEditorCore.cblack= [
25648       //  'font-size' -- this can be set by the project 
25649 ];
25650
25651
25652 Roo.HtmlEditorCore.swapCodes   =[ 
25653     [    8211, "--" ], 
25654     [    8212, "--" ], 
25655     [    8216,  "'" ],  
25656     [    8217, "'" ],  
25657     [    8220, '"' ],  
25658     [    8221, '"' ],  
25659     [    8226, "*" ],  
25660     [    8230, "..." ]
25661 ]; 
25662
25663     /*
25664  * - LGPL
25665  *
25666  * HtmlEditor
25667  * 
25668  */
25669
25670 /**
25671  * @class Roo.bootstrap.HtmlEditor
25672  * @extends Roo.bootstrap.TextArea
25673  * Bootstrap HtmlEditor class
25674
25675  * @constructor
25676  * Create a new HtmlEditor
25677  * @param {Object} config The config object
25678  */
25679
25680 Roo.bootstrap.HtmlEditor = function(config){
25681     Roo.bootstrap.HtmlEditor.superclass.constructor.call(this, config);
25682     if (!this.toolbars) {
25683         this.toolbars = [];
25684     }
25685     
25686     this.editorcore = new Roo.HtmlEditorCore(Roo.apply({ owner : this} , config));
25687     this.addEvents({
25688             /**
25689              * @event initialize
25690              * Fires when the editor is fully initialized (including the iframe)
25691              * @param {HtmlEditor} this
25692              */
25693             initialize: true,
25694             /**
25695              * @event activate
25696              * Fires when the editor is first receives the focus. Any insertion must wait
25697              * until after this event.
25698              * @param {HtmlEditor} this
25699              */
25700             activate: true,
25701              /**
25702              * @event beforesync
25703              * Fires before the textarea is updated with content from the editor iframe. Return false
25704              * to cancel the sync.
25705              * @param {HtmlEditor} this
25706              * @param {String} html
25707              */
25708             beforesync: true,
25709              /**
25710              * @event beforepush
25711              * Fires before the iframe editor is updated with content from the textarea. Return false
25712              * to cancel the push.
25713              * @param {HtmlEditor} this
25714              * @param {String} html
25715              */
25716             beforepush: true,
25717              /**
25718              * @event sync
25719              * Fires when the textarea is updated with content from the editor iframe.
25720              * @param {HtmlEditor} this
25721              * @param {String} html
25722              */
25723             sync: true,
25724              /**
25725              * @event push
25726              * Fires when the iframe editor is updated with content from the textarea.
25727              * @param {HtmlEditor} this
25728              * @param {String} html
25729              */
25730             push: true,
25731              /**
25732              * @event editmodechange
25733              * Fires when the editor switches edit modes
25734              * @param {HtmlEditor} this
25735              * @param {Boolean} sourceEdit True if source edit, false if standard editing.
25736              */
25737             editmodechange: true,
25738             /**
25739              * @event editorevent
25740              * Fires when on any editor (mouse up/down cursor movement etc.) - used for toolbar hooks.
25741              * @param {HtmlEditor} this
25742              */
25743             editorevent: true,
25744             /**
25745              * @event firstfocus
25746              * Fires when on first focus - needed by toolbars..
25747              * @param {HtmlEditor} this
25748              */
25749             firstfocus: true,
25750             /**
25751              * @event autosave
25752              * Auto save the htmlEditor value as a file into Events
25753              * @param {HtmlEditor} this
25754              */
25755             autosave: true,
25756             /**
25757              * @event savedpreview
25758              * preview the saved version of htmlEditor
25759              * @param {HtmlEditor} this
25760              */
25761             savedpreview: true
25762         });
25763 };
25764
25765
25766 Roo.extend(Roo.bootstrap.HtmlEditor, Roo.bootstrap.TextArea,  {
25767     
25768     
25769       /**
25770      * @cfg {Array} toolbars Array of toolbars. - defaults to just the Standard one
25771      */
25772     toolbars : false,
25773     
25774      /**
25775     * @cfg {Array} buttons Array of toolbar's buttons. - defaults to empty
25776     */
25777     btns : [],
25778    
25779      /**
25780      * @cfg {String} resizable  's' or 'se' or 'e' - wrapps the element in a
25781      *                        Roo.resizable.
25782      */
25783     resizable : false,
25784      /**
25785      * @cfg {Number} height (in pixels)
25786      */   
25787     height: 300,
25788    /**
25789      * @cfg {Number} width (in pixels)
25790      */   
25791     width: false,
25792     
25793     /**
25794      * @cfg {Array} stylesheets url of stylesheets. set to [] to disable stylesheets.
25795      * 
25796      */
25797     stylesheets: false,
25798     
25799     // id of frame..
25800     frameId: false,
25801     
25802     // private properties
25803     validationEvent : false,
25804     deferHeight: true,
25805     initialized : false,
25806     activated : false,
25807     
25808     onFocus : Roo.emptyFn,
25809     iframePad:3,
25810     hideMode:'offsets',
25811     
25812     tbContainer : false,
25813     
25814     bodyCls : '',
25815     
25816     toolbarContainer :function() {
25817         return this.wrap.select('.x-html-editor-tb',true).first();
25818     },
25819
25820     /**
25821      * Protected method that will not generally be called directly. It
25822      * is called when the editor creates its toolbar. Override this method if you need to
25823      * add custom toolbar buttons.
25824      * @param {HtmlEditor} editor
25825      */
25826     createToolbar : function(){
25827         Roo.log('renewing');
25828         Roo.log("create toolbars");
25829         
25830         this.toolbars = [ new Roo.bootstrap.htmleditor.ToolbarStandard({editor: this} ) ];
25831         this.toolbars[0].render(this.toolbarContainer());
25832         
25833         return;
25834         
25835 //        if (!editor.toolbars || !editor.toolbars.length) {
25836 //            editor.toolbars = [ new Roo.bootstrap.HtmlEditor.ToolbarStandard() ]; // can be empty?
25837 //        }
25838 //        
25839 //        for (var i =0 ; i < editor.toolbars.length;i++) {
25840 //            editor.toolbars[i] = Roo.factory(
25841 //                    typeof(editor.toolbars[i]) == 'string' ?
25842 //                        { xtype: editor.toolbars[i]} : editor.toolbars[i],
25843 //                Roo.bootstrap.HtmlEditor);
25844 //            editor.toolbars[i].init(editor);
25845 //        }
25846     },
25847
25848      
25849     // private
25850     onRender : function(ct, position)
25851     {
25852        // Roo.log("Call onRender: " + this.xtype);
25853         var _t = this;
25854         Roo.bootstrap.HtmlEditor.superclass.onRender.call(this, ct, position);
25855       
25856         this.wrap = this.inputEl().wrap({
25857             cls:'x-html-editor-wrap', cn:{cls:'x-html-editor-tb'}
25858         });
25859         
25860         this.editorcore.onRender(ct, position);
25861          
25862         if (this.resizable) {
25863             this.resizeEl = new Roo.Resizable(this.wrap, {
25864                 pinned : true,
25865                 wrap: true,
25866                 dynamic : true,
25867                 minHeight : this.height,
25868                 height: this.height,
25869                 handles : this.resizable,
25870                 width: this.width,
25871                 listeners : {
25872                     resize : function(r, w, h) {
25873                         _t.onResize(w,h); // -something
25874                     }
25875                 }
25876             });
25877             
25878         }
25879         this.createToolbar(this);
25880        
25881         
25882         if(!this.width && this.resizable){
25883             this.setSize(this.wrap.getSize());
25884         }
25885         if (this.resizeEl) {
25886             this.resizeEl.resizeTo.defer(100, this.resizeEl,[ this.width,this.height ] );
25887             // should trigger onReize..
25888         }
25889         
25890     },
25891
25892     // private
25893     onResize : function(w, h)
25894     {
25895         Roo.log('resize: ' +w + ',' + h );
25896         Roo.bootstrap.HtmlEditor.superclass.onResize.apply(this, arguments);
25897         var ew = false;
25898         var eh = false;
25899         
25900         if(this.inputEl() ){
25901             if(typeof w == 'number'){
25902                 var aw = w - this.wrap.getFrameWidth('lr');
25903                 this.inputEl().setWidth(this.adjustWidth('textarea', aw));
25904                 ew = aw;
25905             }
25906             if(typeof h == 'number'){
25907                  var tbh = -11;  // fixme it needs to tool bar size!
25908                 for (var i =0; i < this.toolbars.length;i++) {
25909                     // fixme - ask toolbars for heights?
25910                     tbh += this.toolbars[i].el.getHeight();
25911                     //if (this.toolbars[i].footer) {
25912                     //    tbh += this.toolbars[i].footer.el.getHeight();
25913                     //}
25914                 }
25915               
25916                 
25917                 
25918                 
25919                 
25920                 var ah = h - this.wrap.getFrameWidth('tb') - tbh;// this.tb.el.getHeight();
25921                 ah -= 5; // knock a few pixes off for look..
25922                 this.inputEl().setHeight(this.adjustWidth('textarea', ah));
25923                 var eh = ah;
25924             }
25925         }
25926         Roo.log('onResize:' + [w,h,ew,eh].join(',') );
25927         this.editorcore.onResize(ew,eh);
25928         
25929     },
25930
25931     /**
25932      * Toggles the editor between standard and source edit mode.
25933      * @param {Boolean} sourceEdit (optional) True for source edit, false for standard
25934      */
25935     toggleSourceEdit : function(sourceEditMode)
25936     {
25937         this.editorcore.toggleSourceEdit(sourceEditMode);
25938         
25939         if(this.editorcore.sourceEditMode){
25940             Roo.log('editor - showing textarea');
25941             
25942 //            Roo.log('in');
25943 //            Roo.log(this.syncValue());
25944             this.syncValue();
25945             this.inputEl().removeClass(['hide', 'x-hidden']);
25946             this.inputEl().dom.removeAttribute('tabIndex');
25947             this.inputEl().focus();
25948         }else{
25949             Roo.log('editor - hiding textarea');
25950 //            Roo.log('out')
25951 //            Roo.log(this.pushValue()); 
25952             this.pushValue();
25953             
25954             this.inputEl().addClass(['hide', 'x-hidden']);
25955             this.inputEl().dom.setAttribute('tabIndex', -1);
25956             //this.deferFocus();
25957         }
25958          
25959         if(this.resizable){
25960             this.setSize(this.wrap.getSize());
25961         }
25962         
25963         this.fireEvent('editmodechange', this, this.editorcore.sourceEditMode);
25964     },
25965  
25966     // private (for BoxComponent)
25967     adjustSize : Roo.BoxComponent.prototype.adjustSize,
25968
25969     // private (for BoxComponent)
25970     getResizeEl : function(){
25971         return this.wrap;
25972     },
25973
25974     // private (for BoxComponent)
25975     getPositionEl : function(){
25976         return this.wrap;
25977     },
25978
25979     // private
25980     initEvents : function(){
25981         this.originalValue = this.getValue();
25982     },
25983
25984 //    /**
25985 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25986 //     * @method
25987 //     */
25988 //    markInvalid : Roo.emptyFn,
25989 //    /**
25990 //     * Overridden and disabled. The editor element does not support standard valid/invalid marking. @hide
25991 //     * @method
25992 //     */
25993 //    clearInvalid : Roo.emptyFn,
25994
25995     setValue : function(v){
25996         Roo.bootstrap.HtmlEditor.superclass.setValue.call(this, v);
25997         this.editorcore.pushValue();
25998     },
25999
26000      
26001     // private
26002     deferFocus : function(){
26003         this.focus.defer(10, this);
26004     },
26005
26006     // doc'ed in Field
26007     focus : function(){
26008         this.editorcore.focus();
26009         
26010     },
26011       
26012
26013     // private
26014     onDestroy : function(){
26015         
26016         
26017         
26018         if(this.rendered){
26019             
26020             for (var i =0; i < this.toolbars.length;i++) {
26021                 // fixme - ask toolbars for heights?
26022                 this.toolbars[i].onDestroy();
26023             }
26024             
26025             this.wrap.dom.innerHTML = '';
26026             this.wrap.remove();
26027         }
26028     },
26029
26030     // private
26031     onFirstFocus : function(){
26032         //Roo.log("onFirstFocus");
26033         this.editorcore.onFirstFocus();
26034          for (var i =0; i < this.toolbars.length;i++) {
26035             this.toolbars[i].onFirstFocus();
26036         }
26037         
26038     },
26039     
26040     // private
26041     syncValue : function()
26042     {   
26043         this.editorcore.syncValue();
26044     },
26045     
26046     pushValue : function()
26047     {   
26048         this.editorcore.pushValue();
26049     }
26050      
26051     
26052     // hide stuff that is not compatible
26053     /**
26054      * @event blur
26055      * @hide
26056      */
26057     /**
26058      * @event change
26059      * @hide
26060      */
26061     /**
26062      * @event focus
26063      * @hide
26064      */
26065     /**
26066      * @event specialkey
26067      * @hide
26068      */
26069     /**
26070      * @cfg {String} fieldClass @hide
26071      */
26072     /**
26073      * @cfg {String} focusClass @hide
26074      */
26075     /**
26076      * @cfg {String} autoCreate @hide
26077      */
26078     /**
26079      * @cfg {String} inputType @hide
26080      */
26081      
26082     /**
26083      * @cfg {String} invalidText @hide
26084      */
26085     /**
26086      * @cfg {String} msgFx @hide
26087      */
26088     /**
26089      * @cfg {String} validateOnBlur @hide
26090      */
26091 });
26092  
26093     
26094    
26095    
26096    
26097       
26098 Roo.namespace('Roo.bootstrap.htmleditor');
26099 /**
26100  * @class Roo.bootstrap.HtmlEditorToolbar1
26101  * Basic Toolbar
26102  * 
26103  * @example
26104  * Usage:
26105  *
26106  new Roo.bootstrap.HtmlEditor({
26107     ....
26108     toolbars : [
26109         new Roo.bootstrap.HtmlEditorToolbar1({
26110             disable : { fonts: 1 , format: 1, ..., ... , ...],
26111             btns : [ .... ]
26112         })
26113     }
26114      
26115  * 
26116  * @cfg {Object} disable List of elements to disable..
26117  * @cfg {Array} btns List of additional buttons.
26118  * 
26119  * 
26120  * NEEDS Extra CSS? 
26121  * .x-html-editor-tb .x-edit-none .x-btn-text { background: none; }
26122  */
26123  
26124 Roo.bootstrap.htmleditor.ToolbarStandard = function(config)
26125 {
26126     
26127     Roo.apply(this, config);
26128     
26129     // default disabled, based on 'good practice'..
26130     this.disable = this.disable || {};
26131     Roo.applyIf(this.disable, {
26132         fontSize : true,
26133         colors : true,
26134         specialElements : true
26135     });
26136     Roo.bootstrap.htmleditor.ToolbarStandard.superclass.constructor.call(this, config);
26137     
26138     this.editor = config.editor;
26139     this.editorcore = config.editor.editorcore;
26140     
26141     this.buttons   = new Roo.util.MixedCollection(false, function(o) { return o.cmd; });
26142     
26143     //Roo.form.HtmlEditorToolbar1.superclass.constructor.call(this, editor.wrap.dom.firstChild, [], config);
26144     // dont call parent... till later.
26145 }
26146 Roo.extend(Roo.bootstrap.htmleditor.ToolbarStandard, Roo.bootstrap.NavSimplebar,  {
26147      
26148     bar : true,
26149     
26150     editor : false,
26151     editorcore : false,
26152     
26153     
26154     formats : [
26155         "p" ,  
26156         "h1","h2","h3","h4","h5","h6", 
26157         "pre", "code", 
26158         "abbr", "acronym", "address", "cite", "samp", "var",
26159         'div','span'
26160     ],
26161     
26162     onRender : function(ct, position)
26163     {
26164        // Roo.log("Call onRender: " + this.xtype);
26165         
26166        Roo.bootstrap.htmleditor.ToolbarStandard.superclass.onRender.call(this, ct, position);
26167        Roo.log(this.el);
26168        this.el.dom.style.marginBottom = '0';
26169        var _this = this;
26170        var editorcore = this.editorcore;
26171        var editor= this.editor;
26172        
26173        var children = [];
26174        var btn = function(id,cmd , toggle, handler, html){
26175        
26176             var  event = toggle ? 'toggle' : 'click';
26177        
26178             var a = {
26179                 size : 'sm',
26180                 xtype: 'Button',
26181                 xns: Roo.bootstrap,
26182                 //glyphicon : id,
26183                 fa: id,
26184                 cmd : id || cmd,
26185                 enableToggle:toggle !== false,
26186                 html : html || '',
26187                 pressed : toggle ? false : null,
26188                 listeners : {}
26189             };
26190             a.listeners[toggle ? 'toggle' : 'click'] = function() {
26191                 handler ? handler.call(_this,this) :_this.onBtnClick.call(_this, cmd ||  id);
26192             };
26193             children.push(a);
26194             return a;
26195        }
26196        
26197     //    var cb_box = function...
26198         
26199         var style = {
26200                 xtype: 'Button',
26201                 size : 'sm',
26202                 xns: Roo.bootstrap,
26203                 fa : 'font',
26204                 //html : 'submit'
26205                 menu : {
26206                     xtype: 'Menu',
26207                     xns: Roo.bootstrap,
26208                     items:  []
26209                 }
26210         };
26211         Roo.each(this.formats, function(f) {
26212             style.menu.items.push({
26213                 xtype :'MenuItem',
26214                 xns: Roo.bootstrap,
26215                 html : '<'+ f+' style="margin:2px">'+f +'</'+ f+'>',
26216                 tagname : f,
26217                 listeners : {
26218                     click : function()
26219                     {
26220                         editorcore.insertTag(this.tagname);
26221                         editor.focus();
26222                     }
26223                 }
26224                 
26225             });
26226         });
26227         children.push(style);   
26228         
26229         btn('bold',false,true);
26230         btn('italic',false,true);
26231         btn('align-left', 'justifyleft',true);
26232         btn('align-center', 'justifycenter',true);
26233         btn('align-right' , 'justifyright',true);
26234         btn('link', false, false, function(btn) {
26235             //Roo.log("create link?");
26236             var url = prompt(this.createLinkText, this.defaultLinkValue);
26237             if(url && url != 'http:/'+'/'){
26238                 this.editorcore.relayCmd('createlink', url);
26239             }
26240         }),
26241         btn('list','insertunorderedlist',true);
26242         btn('pencil', false,true, function(btn){
26243                 Roo.log(this);
26244                 this.toggleSourceEdit(btn.pressed);
26245         });
26246         
26247         if (this.editor.btns.length > 0) {
26248             for (var i = 0; i<this.editor.btns.length; i++) {
26249                 children.push(this.editor.btns[i]);
26250             }
26251         }
26252         
26253         /*
26254         var cog = {
26255                 xtype: 'Button',
26256                 size : 'sm',
26257                 xns: Roo.bootstrap,
26258                 glyphicon : 'cog',
26259                 //html : 'submit'
26260                 menu : {
26261                     xtype: 'Menu',
26262                     xns: Roo.bootstrap,
26263                     items:  []
26264                 }
26265         };
26266         
26267         cog.menu.items.push({
26268             xtype :'MenuItem',
26269             xns: Roo.bootstrap,
26270             html : Clean styles,
26271             tagname : f,
26272             listeners : {
26273                 click : function()
26274                 {
26275                     editorcore.insertTag(this.tagname);
26276                     editor.focus();
26277                 }
26278             }
26279             
26280         });
26281        */
26282         
26283          
26284        this.xtype = 'NavSimplebar';
26285         
26286         for(var i=0;i< children.length;i++) {
26287             
26288             this.buttons.add(this.addxtypeChild(children[i]));
26289             
26290         }
26291         
26292         editor.on('editorevent', this.updateToolbar, this);
26293     },
26294     onBtnClick : function(id)
26295     {
26296        this.editorcore.relayCmd(id);
26297        this.editorcore.focus();
26298     },
26299     
26300     /**
26301      * Protected method that will not generally be called directly. It triggers
26302      * a toolbar update by reading the markup state of the current selection in the editor.
26303      */
26304     updateToolbar: function(){
26305
26306         if(!this.editorcore.activated){
26307             this.editor.onFirstFocus(); // is this neeed?
26308             return;
26309         }
26310
26311         var btns = this.buttons; 
26312         var doc = this.editorcore.doc;
26313         btns.get('bold').setActive(doc.queryCommandState('bold'));
26314         btns.get('italic').setActive(doc.queryCommandState('italic'));
26315         //btns.get('underline').setActive(doc.queryCommandState('underline'));
26316         
26317         btns.get('align-left').setActive(doc.queryCommandState('justifyleft'));
26318         btns.get('align-center').setActive(doc.queryCommandState('justifycenter'));
26319         btns.get('align-right').setActive(doc.queryCommandState('justifyright'));
26320         
26321         //btns[frameId + '-insertorderedlist').setActive(doc.queryCommandState('insertorderedlist'));
26322         btns.get('list').setActive(doc.queryCommandState('insertunorderedlist'));
26323          /*
26324         
26325         var ans = this.editorcore.getAllAncestors();
26326         if (this.formatCombo) {
26327             
26328             
26329             var store = this.formatCombo.store;
26330             this.formatCombo.setValue("");
26331             for (var i =0; i < ans.length;i++) {
26332                 if (ans[i] && store.query('tag',ans[i].tagName.toLowerCase(), false).length) {
26333                     // select it..
26334                     this.formatCombo.setValue(ans[i].tagName.toLowerCase());
26335                     break;
26336                 }
26337             }
26338         }
26339         
26340         
26341         
26342         // hides menus... - so this cant be on a menu...
26343         Roo.bootstrap.MenuMgr.hideAll();
26344         */
26345         Roo.bootstrap.MenuMgr.hideAll();
26346         //this.editorsyncValue();
26347     },
26348     onFirstFocus: function() {
26349         this.buttons.each(function(item){
26350            item.enable();
26351         });
26352     },
26353     toggleSourceEdit : function(sourceEditMode){
26354         
26355           
26356         if(sourceEditMode){
26357             Roo.log("disabling buttons");
26358            this.buttons.each( function(item){
26359                 if(item.cmd != 'pencil'){
26360                     item.disable();
26361                 }
26362             });
26363           
26364         }else{
26365             Roo.log("enabling buttons");
26366             if(this.editorcore.initialized){
26367                 this.buttons.each( function(item){
26368                     item.enable();
26369                 });
26370             }
26371             
26372         }
26373         Roo.log("calling toggole on editor");
26374         // tell the editor that it's been pressed..
26375         this.editor.toggleSourceEdit(sourceEditMode);
26376        
26377     }
26378 });
26379
26380
26381
26382
26383  
26384 /*
26385  * - LGPL
26386  */
26387
26388 /**
26389  * @class Roo.bootstrap.Markdown
26390  * @extends Roo.bootstrap.TextArea
26391  * Bootstrap Showdown editable area
26392  * @cfg {string} content
26393  * 
26394  * @constructor
26395  * Create a new Showdown
26396  */
26397
26398 Roo.bootstrap.Markdown = function(config){
26399     Roo.bootstrap.Markdown.superclass.constructor.call(this, config);
26400    
26401 };
26402
26403 Roo.extend(Roo.bootstrap.Markdown, Roo.bootstrap.TextArea,  {
26404     
26405     editing :false,
26406     
26407     initEvents : function()
26408     {
26409         
26410         Roo.bootstrap.TextArea.prototype.initEvents.call(this);
26411         this.markdownEl = this.el.createChild({
26412             cls : 'roo-markdown-area'
26413         });
26414         this.inputEl().addClass('d-none');
26415         if (this.getValue() == '') {
26416             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26417             
26418         } else {
26419             this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26420         }
26421         this.markdownEl.on('click', this.toggleTextEdit, this);
26422         this.on('blur', this.toggleTextEdit, this);
26423         this.on('specialkey', this.resizeTextArea, this);
26424     },
26425     
26426     toggleTextEdit : function()
26427     {
26428         var sh = this.markdownEl.getHeight();
26429         this.inputEl().addClass('d-none');
26430         this.markdownEl.addClass('d-none');
26431         if (!this.editing) {
26432             // show editor?
26433             this.inputEl().setHeight(Math.min(500, Math.max(sh,(this.getValue().split("\n").length+1) * 30)));
26434             this.inputEl().removeClass('d-none');
26435             this.inputEl().focus();
26436             this.editing = true;
26437             return;
26438         }
26439         // show showdown...
26440         this.updateMarkdown();
26441         this.markdownEl.removeClass('d-none');
26442         this.editing = false;
26443         return;
26444     },
26445     updateMarkdown : function()
26446     {
26447         if (this.getValue() == '') {
26448             this.markdownEl.dom.innerHTML = String.format('<span class="roo-placeholder">{0}</span>', this.placeholder || '');
26449             return;
26450         }
26451  
26452         this.markdownEl.dom.innerHTML = Roo.Markdown.toHtml(Roo.util.Format.htmlEncode(this.getValue()));
26453     },
26454     
26455     resizeTextArea: function () {
26456         
26457         var sh = 100;
26458         Roo.log([sh, this.getValue().split("\n").length * 30]);
26459         this.inputEl().setHeight(Math.min(500, Math.max(sh, (this.getValue().split("\n").length +1) * 30)));
26460     },
26461     setValue : function(val)
26462     {
26463         Roo.bootstrap.TextArea.prototype.setValue.call(this,val);
26464         if (!this.editing) {
26465             this.updateMarkdown();
26466         }
26467         
26468     },
26469     focus : function()
26470     {
26471         if (!this.editing) {
26472             this.toggleTextEdit();
26473         }
26474         
26475     }
26476
26477
26478 });
26479 /**
26480  * @class Roo.bootstrap.Table.AbstractSelectionModel
26481  * @extends Roo.util.Observable
26482  * Abstract base class for grid SelectionModels.  It provides the interface that should be
26483  * implemented by descendant classes.  This class should not be directly instantiated.
26484  * @constructor
26485  */
26486 Roo.bootstrap.Table.AbstractSelectionModel = function(){
26487     this.locked = false;
26488     Roo.bootstrap.Table.AbstractSelectionModel.superclass.constructor.call(this);
26489 };
26490
26491
26492 Roo.extend(Roo.bootstrap.Table.AbstractSelectionModel, Roo.util.Observable,  {
26493     /** @ignore Called by the grid automatically. Do not call directly. */
26494     init : function(grid){
26495         this.grid = grid;
26496         this.initEvents();
26497     },
26498
26499     /**
26500      * Locks the selections.
26501      */
26502     lock : function(){
26503         this.locked = true;
26504     },
26505
26506     /**
26507      * Unlocks the selections.
26508      */
26509     unlock : function(){
26510         this.locked = false;
26511     },
26512
26513     /**
26514      * Returns true if the selections are locked.
26515      * @return {Boolean}
26516      */
26517     isLocked : function(){
26518         return this.locked;
26519     },
26520     
26521     
26522     initEvents : function ()
26523     {
26524         
26525     }
26526 });
26527 /**
26528  * @extends Roo.bootstrap.Table.AbstractSelectionModel
26529  * @class Roo.bootstrap.Table.RowSelectionModel
26530  * The default SelectionModel used by {@link Roo.bootstrap.Table}.
26531  * It supports multiple selections and keyboard selection/navigation. 
26532  * @constructor
26533  * @param {Object} config
26534  */
26535
26536 Roo.bootstrap.Table.RowSelectionModel = function(config){
26537     Roo.apply(this, config);
26538     this.selections = new Roo.util.MixedCollection(false, function(o){
26539         return o.id;
26540     });
26541
26542     this.last = false;
26543     this.lastActive = false;
26544
26545     this.addEvents({
26546         /**
26547              * @event selectionchange
26548              * Fires when the selection changes
26549              * @param {SelectionModel} this
26550              */
26551             "selectionchange" : true,
26552         /**
26553              * @event afterselectionchange
26554              * Fires after the selection changes (eg. by key press or clicking)
26555              * @param {SelectionModel} this
26556              */
26557             "afterselectionchange" : true,
26558         /**
26559              * @event beforerowselect
26560              * Fires when a row is selected being selected, return false to cancel.
26561              * @param {SelectionModel} this
26562              * @param {Number} rowIndex The selected index
26563              * @param {Boolean} keepExisting False if other selections will be cleared
26564              */
26565             "beforerowselect" : true,
26566         /**
26567              * @event rowselect
26568              * Fires when a row is selected.
26569              * @param {SelectionModel} this
26570              * @param {Number} rowIndex The selected index
26571              * @param {Roo.data.Record} r The record
26572              */
26573             "rowselect" : true,
26574         /**
26575              * @event rowdeselect
26576              * Fires when a row is deselected.
26577              * @param {SelectionModel} this
26578              * @param {Number} rowIndex The selected index
26579              */
26580         "rowdeselect" : true
26581     });
26582     Roo.bootstrap.Table.RowSelectionModel.superclass.constructor.call(this);
26583     this.locked = false;
26584  };
26585
26586 Roo.extend(Roo.bootstrap.Table.RowSelectionModel, Roo.bootstrap.Table.AbstractSelectionModel,  {
26587     /**
26588      * @cfg {Boolean} singleSelect
26589      * True to allow selection of only one row at a time (defaults to false)
26590      */
26591     singleSelect : false,
26592
26593     // private
26594     initEvents : function()
26595     {
26596
26597         //if(!this.grid.enableDragDrop && !this.grid.enableDrag){
26598         //    this.growclickrid.on("mousedown", this.handleMouseDown, this);
26599         //}else{ // allow click to work like normal
26600          //   this.grid.on("rowclick", this.handleDragableRowClick, this);
26601         //}
26602         //this.grid.on("rowdblclick", this.handleMouseDBClick, this);
26603         this.grid.on("rowclick", this.handleMouseDown, this);
26604         
26605         this.rowNav = new Roo.KeyNav(this.grid.getGridEl(), {
26606             "up" : function(e){
26607                 if(!e.shiftKey){
26608                     this.selectPrevious(e.shiftKey);
26609                 }else if(this.last !== false && this.lastActive !== false){
26610                     var last = this.last;
26611                     this.selectRange(this.last,  this.lastActive-1);
26612                     this.grid.getView().focusRow(this.lastActive);
26613                     if(last !== false){
26614                         this.last = last;
26615                     }
26616                 }else{
26617                     this.selectFirstRow();
26618                 }
26619                 this.fireEvent("afterselectionchange", this);
26620             },
26621             "down" : function(e){
26622                 if(!e.shiftKey){
26623                     this.selectNext(e.shiftKey);
26624                 }else if(this.last !== false && this.lastActive !== false){
26625                     var last = this.last;
26626                     this.selectRange(this.last,  this.lastActive+1);
26627                     this.grid.getView().focusRow(this.lastActive);
26628                     if(last !== false){
26629                         this.last = last;
26630                     }
26631                 }else{
26632                     this.selectFirstRow();
26633                 }
26634                 this.fireEvent("afterselectionchange", this);
26635             },
26636             scope: this
26637         });
26638         this.grid.store.on('load', function(){
26639             this.selections.clear();
26640         },this);
26641         /*
26642         var view = this.grid.view;
26643         view.on("refresh", this.onRefresh, this);
26644         view.on("rowupdated", this.onRowUpdated, this);
26645         view.on("rowremoved", this.onRemove, this);
26646         */
26647     },
26648
26649     // private
26650     onRefresh : function()
26651     {
26652         var ds = this.grid.store, i, v = this.grid.view;
26653         var s = this.selections;
26654         s.each(function(r){
26655             if((i = ds.indexOfId(r.id)) != -1){
26656                 v.onRowSelect(i);
26657             }else{
26658                 s.remove(r);
26659             }
26660         });
26661     },
26662
26663     // private
26664     onRemove : function(v, index, r){
26665         this.selections.remove(r);
26666     },
26667
26668     // private
26669     onRowUpdated : function(v, index, r){
26670         if(this.isSelected(r)){
26671             v.onRowSelect(index);
26672         }
26673     },
26674
26675     /**
26676      * Select records.
26677      * @param {Array} records The records to select
26678      * @param {Boolean} keepExisting (optional) True to keep existing selections
26679      */
26680     selectRecords : function(records, keepExisting)
26681     {
26682         if(!keepExisting){
26683             this.clearSelections();
26684         }
26685             var ds = this.grid.store;
26686         for(var i = 0, len = records.length; i < len; i++){
26687             this.selectRow(ds.indexOf(records[i]), true);
26688         }
26689     },
26690
26691     /**
26692      * Gets the number of selected rows.
26693      * @return {Number}
26694      */
26695     getCount : function(){
26696         return this.selections.length;
26697     },
26698
26699     /**
26700      * Selects the first row in the grid.
26701      */
26702     selectFirstRow : function(){
26703         this.selectRow(0);
26704     },
26705
26706     /**
26707      * Select the last row.
26708      * @param {Boolean} keepExisting (optional) True to keep existing selections
26709      */
26710     selectLastRow : function(keepExisting){
26711         //this.selectRow(this.grid.dataSource.getCount() - 1, keepExisting);
26712         this.selectRow(this.grid.store.getCount() - 1, keepExisting);
26713     },
26714
26715     /**
26716      * Selects the row immediately following the last selected row.
26717      * @param {Boolean} keepExisting (optional) True to keep existing selections
26718      */
26719     selectNext : function(keepExisting)
26720     {
26721             if(this.last !== false && (this.last+1) < this.grid.store.getCount()){
26722             this.selectRow(this.last+1, keepExisting);
26723             this.grid.getView().focusRow(this.last);
26724         }
26725     },
26726
26727     /**
26728      * Selects the row that precedes the last selected row.
26729      * @param {Boolean} keepExisting (optional) True to keep existing selections
26730      */
26731     selectPrevious : function(keepExisting){
26732         if(this.last){
26733             this.selectRow(this.last-1, keepExisting);
26734             this.grid.getView().focusRow(this.last);
26735         }
26736     },
26737
26738     /**
26739      * Returns the selected records
26740      * @return {Array} Array of selected records
26741      */
26742     getSelections : function(){
26743         return [].concat(this.selections.items);
26744     },
26745
26746     /**
26747      * Returns the first selected record.
26748      * @return {Record}
26749      */
26750     getSelected : function(){
26751         return this.selections.itemAt(0);
26752     },
26753
26754
26755     /**
26756      * Clears all selections.
26757      */
26758     clearSelections : function(fast)
26759     {
26760         if(this.locked) {
26761             return;
26762         }
26763         if(fast !== true){
26764                 var ds = this.grid.store;
26765             var s = this.selections;
26766             s.each(function(r){
26767                 this.deselectRow(ds.indexOfId(r.id));
26768             }, this);
26769             s.clear();
26770         }else{
26771             this.selections.clear();
26772         }
26773         this.last = false;
26774     },
26775
26776
26777     /**
26778      * Selects all rows.
26779      */
26780     selectAll : function(){
26781         if(this.locked) {
26782             return;
26783         }
26784         this.selections.clear();
26785         for(var i = 0, len = this.grid.store.getCount(); i < len; i++){
26786             this.selectRow(i, true);
26787         }
26788     },
26789
26790     /**
26791      * Returns True if there is a selection.
26792      * @return {Boolean}
26793      */
26794     hasSelection : function(){
26795         return this.selections.length > 0;
26796     },
26797
26798     /**
26799      * Returns True if the specified row is selected.
26800      * @param {Number/Record} record The record or index of the record to check
26801      * @return {Boolean}
26802      */
26803     isSelected : function(index){
26804             var r = typeof index == "number" ? this.grid.store.getAt(index) : index;
26805         return (r && this.selections.key(r.id) ? true : false);
26806     },
26807
26808     /**
26809      * Returns True if the specified record id is selected.
26810      * @param {String} id The id of record to check
26811      * @return {Boolean}
26812      */
26813     isIdSelected : function(id){
26814         return (this.selections.key(id) ? true : false);
26815     },
26816
26817
26818     // private
26819     handleMouseDBClick : function(e, t){
26820         
26821     },
26822     // private
26823     handleMouseDown : function(e, t)
26824     {
26825             var rowIndex = this.grid.headerShow  ? t.dom.rowIndex - 1 : t.dom.rowIndex ; // first row is header???
26826         if(this.isLocked() || rowIndex < 0 ){
26827             return;
26828         };
26829         if(e.shiftKey && this.last !== false){
26830             var last = this.last;
26831             this.selectRange(last, rowIndex, e.ctrlKey);
26832             this.last = last; // reset the last
26833             t.focus();
26834     
26835         }else{
26836             var isSelected = this.isSelected(rowIndex);
26837             //Roo.log("select row:" + rowIndex);
26838             if(isSelected){
26839                 this.deselectRow(rowIndex);
26840             } else {
26841                         this.selectRow(rowIndex, true);
26842             }
26843     
26844             /*
26845                 if(e.button !== 0 && isSelected){
26846                 alert('rowIndex 2: ' + rowIndex);
26847                     view.focusRow(rowIndex);
26848                 }else if(e.ctrlKey && isSelected){
26849                     this.deselectRow(rowIndex);
26850                 }else if(!isSelected){
26851                     this.selectRow(rowIndex, e.button === 0 && (e.ctrlKey || e.shiftKey));
26852                     view.focusRow(rowIndex);
26853                 }
26854             */
26855         }
26856         this.fireEvent("afterselectionchange", this);
26857     },
26858     // private
26859     handleDragableRowClick :  function(grid, rowIndex, e) 
26860     {
26861         if(e.button === 0 && !e.shiftKey && !e.ctrlKey) {
26862             this.selectRow(rowIndex, false);
26863             grid.view.focusRow(rowIndex);
26864              this.fireEvent("afterselectionchange", this);
26865         }
26866     },
26867     
26868     /**
26869      * Selects multiple rows.
26870      * @param {Array} rows Array of the indexes of the row to select
26871      * @param {Boolean} keepExisting (optional) True to keep existing selections
26872      */
26873     selectRows : function(rows, keepExisting){
26874         if(!keepExisting){
26875             this.clearSelections();
26876         }
26877         for(var i = 0, len = rows.length; i < len; i++){
26878             this.selectRow(rows[i], true);
26879         }
26880     },
26881
26882     /**
26883      * Selects a range of rows. All rows in between startRow and endRow are also selected.
26884      * @param {Number} startRow The index of the first row in the range
26885      * @param {Number} endRow The index of the last row in the range
26886      * @param {Boolean} keepExisting (optional) True to retain existing selections
26887      */
26888     selectRange : function(startRow, endRow, keepExisting){
26889         if(this.locked) {
26890             return;
26891         }
26892         if(!keepExisting){
26893             this.clearSelections();
26894         }
26895         if(startRow <= endRow){
26896             for(var i = startRow; i <= endRow; i++){
26897                 this.selectRow(i, true);
26898             }
26899         }else{
26900             for(var i = startRow; i >= endRow; i--){
26901                 this.selectRow(i, true);
26902             }
26903         }
26904     },
26905
26906     /**
26907      * Deselects a range of rows. All rows in between startRow and endRow are also deselected.
26908      * @param {Number} startRow The index of the first row in the range
26909      * @param {Number} endRow The index of the last row in the range
26910      */
26911     deselectRange : function(startRow, endRow, preventViewNotify){
26912         if(this.locked) {
26913             return;
26914         }
26915         for(var i = startRow; i <= endRow; i++){
26916             this.deselectRow(i, preventViewNotify);
26917         }
26918     },
26919
26920     /**
26921      * Selects a row.
26922      * @param {Number} row The index of the row to select
26923      * @param {Boolean} keepExisting (optional) True to keep existing selections
26924      */
26925     selectRow : function(index, keepExisting, preventViewNotify)
26926     {
26927             if(this.locked || (index < 0 || index > this.grid.store.getCount())) {
26928             return;
26929         }
26930         if(this.fireEvent("beforerowselect", this, index, keepExisting) !== false){
26931             if(!keepExisting || this.singleSelect){
26932                 this.clearSelections();
26933             }
26934             
26935             var r = this.grid.store.getAt(index);
26936             //console.log('selectRow - record id :' + r.id);
26937             
26938             this.selections.add(r);
26939             this.last = this.lastActive = index;
26940             if(!preventViewNotify){
26941                 var proxy = new Roo.Element(
26942                                 this.grid.getRowDom(index)
26943                 );
26944                 proxy.addClass('bg-info info');
26945             }
26946             this.fireEvent("rowselect", this, index, r);
26947             this.fireEvent("selectionchange", this);
26948         }
26949     },
26950
26951     /**
26952      * Deselects a row.
26953      * @param {Number} row The index of the row to deselect
26954      */
26955     deselectRow : function(index, preventViewNotify)
26956     {
26957         if(this.locked) {
26958             return;
26959         }
26960         if(this.last == index){
26961             this.last = false;
26962         }
26963         if(this.lastActive == index){
26964             this.lastActive = false;
26965         }
26966         
26967         var r = this.grid.store.getAt(index);
26968         if (!r) {
26969             return;
26970         }
26971         
26972         this.selections.remove(r);
26973         //.console.log('deselectRow - record id :' + r.id);
26974         if(!preventViewNotify){
26975         
26976             var proxy = new Roo.Element(
26977                 this.grid.getRowDom(index)
26978             );
26979             proxy.removeClass('bg-info info');
26980         }
26981         this.fireEvent("rowdeselect", this, index);
26982         this.fireEvent("selectionchange", this);
26983     },
26984
26985     // private
26986     restoreLast : function(){
26987         if(this._last){
26988             this.last = this._last;
26989         }
26990     },
26991
26992     // private
26993     acceptsNav : function(row, col, cm){
26994         return !cm.isHidden(col) && cm.isCellEditable(col, row);
26995     },
26996
26997     // private
26998     onEditorKey : function(field, e){
26999         var k = e.getKey(), newCell, g = this.grid, ed = g.activeEditor;
27000         if(k == e.TAB){
27001             e.stopEvent();
27002             ed.completeEdit();
27003             if(e.shiftKey){
27004                 newCell = g.walkCells(ed.row, ed.col-1, -1, this.acceptsNav, this);
27005             }else{
27006                 newCell = g.walkCells(ed.row, ed.col+1, 1, this.acceptsNav, this);
27007             }
27008         }else if(k == e.ENTER && !e.ctrlKey){
27009             e.stopEvent();
27010             ed.completeEdit();
27011             if(e.shiftKey){
27012                 newCell = g.walkCells(ed.row-1, ed.col, -1, this.acceptsNav, this);
27013             }else{
27014                 newCell = g.walkCells(ed.row+1, ed.col, 1, this.acceptsNav, this);
27015             }
27016         }else if(k == e.ESC){
27017             ed.cancelEdit();
27018         }
27019         if(newCell){
27020             g.startEditing(newCell[0], newCell[1]);
27021         }
27022     }
27023 });
27024 /*
27025  * Based on:
27026  * Ext JS Library 1.1.1
27027  * Copyright(c) 2006-2007, Ext JS, LLC.
27028  *
27029  * Originally Released Under LGPL - original licence link has changed is not relivant.
27030  *
27031  * Fork - LGPL
27032  * <script type="text/javascript">
27033  */
27034  
27035 /**
27036  * @class Roo.bootstrap.PagingToolbar
27037  * @extends Roo.bootstrap.NavSimplebar
27038  * A specialized toolbar that is bound to a {@link Roo.data.Store} and provides automatic paging controls.
27039  * @constructor
27040  * Create a new PagingToolbar
27041  * @param {Object} config The config object
27042  * @param {Roo.data.Store} store
27043  */
27044 Roo.bootstrap.PagingToolbar = function(config)
27045 {
27046     // old args format still supported... - xtype is prefered..
27047         // created from xtype...
27048     
27049     this.ds = config.dataSource;
27050     
27051     if (config.store && !this.ds) {
27052         this.store= Roo.factory(config.store, Roo.data);
27053         this.ds = this.store;
27054         this.ds.xmodule = this.xmodule || false;
27055     }
27056     
27057     this.toolbarItems = [];
27058     if (config.items) {
27059         this.toolbarItems = config.items;
27060     }
27061     
27062     Roo.bootstrap.PagingToolbar.superclass.constructor.call(this, config);
27063     
27064     this.cursor = 0;
27065     
27066     if (this.ds) { 
27067         this.bind(this.ds);
27068     }
27069     
27070     if (Roo.bootstrap.version == 4) {
27071         this.navgroup = new Roo.bootstrap.ButtonGroup({ cls: 'pagination' });
27072     } else {
27073         this.navgroup = new Roo.bootstrap.NavGroup({ cls: 'pagination' });
27074     }
27075     
27076 };
27077
27078 Roo.extend(Roo.bootstrap.PagingToolbar, Roo.bootstrap.NavSimplebar, {
27079     /**
27080      * @cfg {Roo.data.Store} dataSource
27081      * The underlying data store providing the paged data
27082      */
27083     /**
27084      * @cfg {String/HTMLElement/Element} container
27085      * container The id or element that will contain the toolbar
27086      */
27087     /**
27088      * @cfg {Boolean} displayInfo
27089      * True to display the displayMsg (defaults to false)
27090      */
27091     /**
27092      * @cfg {Number} pageSize
27093      * The number of records to display per page (defaults to 20)
27094      */
27095     pageSize: 20,
27096     /**
27097      * @cfg {String} displayMsg
27098      * The paging status message to display (defaults to "Displaying {start} - {end} of {total}")
27099      */
27100     displayMsg : 'Displaying {0} - {1} of {2}',
27101     /**
27102      * @cfg {String} emptyMsg
27103      * The message to display when no records are found (defaults to "No data to display")
27104      */
27105     emptyMsg : 'No data to display',
27106     /**
27107      * Customizable piece of the default paging text (defaults to "Page")
27108      * @type String
27109      */
27110     beforePageText : "Page",
27111     /**
27112      * Customizable piece of the default paging text (defaults to "of %0")
27113      * @type String
27114      */
27115     afterPageText : "of {0}",
27116     /**
27117      * Customizable piece of the default paging text (defaults to "First Page")
27118      * @type String
27119      */
27120     firstText : "First Page",
27121     /**
27122      * Customizable piece of the default paging text (defaults to "Previous Page")
27123      * @type String
27124      */
27125     prevText : "Previous Page",
27126     /**
27127      * Customizable piece of the default paging text (defaults to "Next Page")
27128      * @type String
27129      */
27130     nextText : "Next Page",
27131     /**
27132      * Customizable piece of the default paging text (defaults to "Last Page")
27133      * @type String
27134      */
27135     lastText : "Last Page",
27136     /**
27137      * Customizable piece of the default paging text (defaults to "Refresh")
27138      * @type String
27139      */
27140     refreshText : "Refresh",
27141
27142     buttons : false,
27143     // private
27144     onRender : function(ct, position) 
27145     {
27146         Roo.bootstrap.PagingToolbar.superclass.onRender.call(this, ct, position);
27147         this.navgroup.parentId = this.id;
27148         this.navgroup.onRender(this.el, null);
27149         // add the buttons to the navgroup
27150         
27151         if(this.displayInfo){
27152             this.el.select('ul.navbar-nav',true).first().createChild({cls:'x-paging-info'});
27153             this.displayEl = this.el.select('.x-paging-info', true).first();
27154 //            var navel = this.navgroup.addItem( { tagtype : 'span', html : '', cls : 'x-paging-info', preventDefault : true } );
27155 //            this.displayEl = navel.el.select('span',true).first();
27156         }
27157         
27158         var _this = this;
27159         
27160         if(this.buttons){
27161             Roo.each(_this.buttons, function(e){ // this might need to use render????
27162                Roo.factory(e).render(_this.el);
27163             });
27164         }
27165             
27166         Roo.each(_this.toolbarItems, function(e) {
27167             _this.navgroup.addItem(e);
27168         });
27169         
27170         
27171         this.first = this.navgroup.addItem({
27172             tooltip: this.firstText,
27173             cls: "prev btn-outline-secondary",
27174             html : ' <i class="fa fa-step-backward"></i>',
27175             disabled: true,
27176             preventDefault: true,
27177             listeners : { click : this.onClick.createDelegate(this, ["first"]) }
27178         });
27179         
27180         this.prev =  this.navgroup.addItem({
27181             tooltip: this.prevText,
27182             cls: "prev btn-outline-secondary",
27183             html : ' <i class="fa fa-backward"></i>',
27184             disabled: true,
27185             preventDefault: true,
27186             listeners : { click :  this.onClick.createDelegate(this, ["prev"]) }
27187         });
27188     //this.addSeparator();
27189         
27190         
27191         var field = this.navgroup.addItem( {
27192             tagtype : 'span',
27193             cls : 'x-paging-position  btn-outline-secondary',
27194              disabled: true,
27195             html : this.beforePageText  +
27196                 '<input type="text" size="3" value="1" class="x-grid-page-number">' +
27197                 '<span class="x-paging-after">' +  String.format(this.afterPageText, 1) + '</span>'
27198          } ); //?? escaped?
27199         
27200         this.field = field.el.select('input', true).first();
27201         this.field.on("keydown", this.onPagingKeydown, this);
27202         this.field.on("focus", function(){this.dom.select();});
27203     
27204     
27205         this.afterTextEl =  field.el.select('.x-paging-after',true).first();
27206         //this.field.setHeight(18);
27207         //this.addSeparator();
27208         this.next = this.navgroup.addItem({
27209             tooltip: this.nextText,
27210             cls: "next btn-outline-secondary",
27211             html : ' <i class="fa fa-forward"></i>',
27212             disabled: true,
27213             preventDefault: true,
27214             listeners : { click :  this.onClick.createDelegate(this, ["next"]) }
27215         });
27216         this.last = this.navgroup.addItem({
27217             tooltip: this.lastText,
27218             html : ' <i class="fa fa-step-forward"></i>',
27219             cls: "next btn-outline-secondary",
27220             disabled: true,
27221             preventDefault: true,
27222             listeners : { click :  this.onClick.createDelegate(this, ["last"]) }
27223         });
27224     //this.addSeparator();
27225         this.loading = this.navgroup.addItem({
27226             tooltip: this.refreshText,
27227             cls: "btn-outline-secondary",
27228             html : ' <i class="fa fa-refresh"></i>',
27229             preventDefault: true,
27230             listeners : { click : this.onClick.createDelegate(this, ["refresh"]) }
27231         });
27232         
27233     },
27234
27235     // private
27236     updateInfo : function(){
27237         if(this.displayEl){
27238             var count = (typeof(this.getCount) == 'undefined') ? this.ds.getCount() : this.getCount();
27239             var msg = count == 0 ?
27240                 this.emptyMsg :
27241                 String.format(
27242                     this.displayMsg,
27243                     this.cursor+1, this.cursor+count, this.ds.getTotalCount()    
27244                 );
27245             this.displayEl.update(msg);
27246         }
27247     },
27248
27249     // private
27250     onLoad : function(ds, r, o)
27251     {
27252         this.cursor = o.params.start ? o.params.start : 0;
27253         
27254         var d = this.getPageData(),
27255             ap = d.activePage,
27256             ps = d.pages;
27257         
27258         
27259         this.afterTextEl.dom.innerHTML = String.format(this.afterPageText, d.pages);
27260         this.field.dom.value = ap;
27261         this.first.setDisabled(ap == 1);
27262         this.prev.setDisabled(ap == 1);
27263         this.next.setDisabled(ap == ps);
27264         this.last.setDisabled(ap == ps);
27265         this.loading.enable();
27266         this.updateInfo();
27267     },
27268
27269     // private
27270     getPageData : function(){
27271         var total = this.ds.getTotalCount();
27272         return {
27273             total : total,
27274             activePage : Math.ceil((this.cursor+this.pageSize)/this.pageSize),
27275             pages :  total < this.pageSize ? 1 : Math.ceil(total/this.pageSize)
27276         };
27277     },
27278
27279     // private
27280     onLoadError : function(){
27281         this.loading.enable();
27282     },
27283
27284     // private
27285     onPagingKeydown : function(e){
27286         var k = e.getKey();
27287         var d = this.getPageData();
27288         if(k == e.RETURN){
27289             var v = this.field.dom.value, pageNum;
27290             if(!v || isNaN(pageNum = parseInt(v, 10))){
27291                 this.field.dom.value = d.activePage;
27292                 return;
27293             }
27294             pageNum = Math.min(Math.max(1, pageNum), d.pages) - 1;
27295             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27296             e.stopEvent();
27297         }
27298         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))
27299         {
27300           var pageNum = (k == e.HOME || (k == e.DOWN && e.ctrlKey) || (k == e.LEFT && e.ctrlKey) || (k == e.PAGEDOWN && e.ctrlKey)) ? 1 : d.pages;
27301           this.field.dom.value = pageNum;
27302           this.ds.load({params:{start: (pageNum - 1) * this.pageSize, limit: this.pageSize}});
27303           e.stopEvent();
27304         }
27305         else if(k == e.UP || k == e.RIGHT || k == e.PAGEUP || k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN)
27306         {
27307           var v = this.field.dom.value, pageNum; 
27308           var increment = (e.shiftKey) ? 10 : 1;
27309           if(k == e.DOWN || k == e.LEFT || k == e.PAGEDOWN) {
27310                 increment *= -1;
27311           }
27312           if(!v || isNaN(pageNum = parseInt(v, 10))) {
27313             this.field.dom.value = d.activePage;
27314             return;
27315           }
27316           else if(parseInt(v, 10) + increment >= 1 & parseInt(v, 10) + increment <= d.pages)
27317           {
27318             this.field.dom.value = parseInt(v, 10) + increment;
27319             pageNum = Math.min(Math.max(1, pageNum + increment), d.pages) - 1;
27320             this.ds.load({params:{start: pageNum * this.pageSize, limit: this.pageSize}});
27321           }
27322           e.stopEvent();
27323         }
27324     },
27325
27326     // private
27327     beforeLoad : function(){
27328         if(this.loading){
27329             this.loading.disable();
27330         }
27331     },
27332
27333     // private
27334     onClick : function(which){
27335         
27336         var ds = this.ds;
27337         if (!ds) {
27338             return;
27339         }
27340         
27341         switch(which){
27342             case "first":
27343                 ds.load({params:{start: 0, limit: this.pageSize}});
27344             break;
27345             case "prev":
27346                 ds.load({params:{start: Math.max(0, this.cursor-this.pageSize), limit: this.pageSize}});
27347             break;
27348             case "next":
27349                 ds.load({params:{start: this.cursor+this.pageSize, limit: this.pageSize}});
27350             break;
27351             case "last":
27352                 var total = ds.getTotalCount();
27353                 var extra = total % this.pageSize;
27354                 var lastStart = extra ? (total - extra) : total-this.pageSize;
27355                 ds.load({params:{start: lastStart, limit: this.pageSize}});
27356             break;
27357             case "refresh":
27358                 ds.load({params:{start: this.cursor, limit: this.pageSize}});
27359             break;
27360         }
27361     },
27362
27363     /**
27364      * Unbinds the paging toolbar from the specified {@link Roo.data.Store}
27365      * @param {Roo.data.Store} store The data store to unbind
27366      */
27367     unbind : function(ds){
27368         ds.un("beforeload", this.beforeLoad, this);
27369         ds.un("load", this.onLoad, this);
27370         ds.un("loadexception", this.onLoadError, this);
27371         ds.un("remove", this.updateInfo, this);
27372         ds.un("add", this.updateInfo, this);
27373         this.ds = undefined;
27374     },
27375
27376     /**
27377      * Binds the paging toolbar to the specified {@link Roo.data.Store}
27378      * @param {Roo.data.Store} store The data store to bind
27379      */
27380     bind : function(ds){
27381         ds.on("beforeload", this.beforeLoad, this);
27382         ds.on("load", this.onLoad, this);
27383         ds.on("loadexception", this.onLoadError, this);
27384         ds.on("remove", this.updateInfo, this);
27385         ds.on("add", this.updateInfo, this);
27386         this.ds = ds;
27387     }
27388 });/*
27389  * - LGPL
27390  *
27391  * element
27392  * 
27393  */
27394
27395 /**
27396  * @class Roo.bootstrap.MessageBar
27397  * @extends Roo.bootstrap.Component
27398  * Bootstrap MessageBar class
27399  * @cfg {String} html contents of the MessageBar
27400  * @cfg {String} weight (info | success | warning | danger) default info
27401  * @cfg {String} beforeClass insert the bar before the given class
27402  * @cfg {Boolean} closable (true | false) default false
27403  * @cfg {Boolean} fixed (true | false) default false, fix the bar at the top
27404  * 
27405  * @constructor
27406  * Create a new Element
27407  * @param {Object} config The config object
27408  */
27409
27410 Roo.bootstrap.MessageBar = function(config){
27411     Roo.bootstrap.MessageBar.superclass.constructor.call(this, config);
27412 };
27413
27414 Roo.extend(Roo.bootstrap.MessageBar, Roo.bootstrap.Component,  {
27415     
27416     html: '',
27417     weight: 'info',
27418     closable: false,
27419     fixed: false,
27420     beforeClass: 'bootstrap-sticky-wrap',
27421     
27422     getAutoCreate : function(){
27423         
27424         var cfg = {
27425             tag: 'div',
27426             cls: 'alert alert-dismissable alert-' + this.weight,
27427             cn: [
27428                 {
27429                     tag: 'span',
27430                     cls: 'message',
27431                     html: this.html || ''
27432                 }
27433             ]
27434         };
27435         
27436         if(this.fixed){
27437             cfg.cls += ' alert-messages-fixed';
27438         }
27439         
27440         if(this.closable){
27441             cfg.cn.push({
27442                 tag: 'button',
27443                 cls: 'close',
27444                 html: 'x'
27445             });
27446         }
27447         
27448         return cfg;
27449     },
27450     
27451     onRender : function(ct, position)
27452     {
27453         Roo.bootstrap.Component.superclass.onRender.call(this, ct, position);
27454         
27455         if(!this.el){
27456             var cfg = Roo.apply({},  this.getAutoCreate());
27457             cfg.id = Roo.id();
27458             
27459             if (this.cls) {
27460                 cfg.cls += ' ' + this.cls;
27461             }
27462             if (this.style) {
27463                 cfg.style = this.style;
27464             }
27465             this.el = Roo.get(document.body).createChild(cfg, Roo.select('.'+this.beforeClass, true).first());
27466             
27467             this.el.setVisibilityMode(Roo.Element.DISPLAY);
27468         }
27469         
27470         this.el.select('>button.close').on('click', this.hide, this);
27471         
27472     },
27473     
27474     show : function()
27475     {
27476         if (!this.rendered) {
27477             this.render();
27478         }
27479         
27480         this.el.show();
27481         
27482         this.fireEvent('show', this);
27483         
27484     },
27485     
27486     hide : function()
27487     {
27488         if (!this.rendered) {
27489             this.render();
27490         }
27491         
27492         this.el.hide();
27493         
27494         this.fireEvent('hide', this);
27495     },
27496     
27497     update : function()
27498     {
27499 //        var e = this.el.dom.firstChild;
27500 //        
27501 //        if(this.closable){
27502 //            e = e.nextSibling;
27503 //        }
27504 //        
27505 //        e.data = this.html || '';
27506
27507         this.el.select('>.message', true).first().dom.innerHTML = this.html || '';
27508     }
27509    
27510 });
27511
27512  
27513
27514      /*
27515  * - LGPL
27516  *
27517  * Graph
27518  * 
27519  */
27520
27521
27522 /**
27523  * @class Roo.bootstrap.Graph
27524  * @extends Roo.bootstrap.Component
27525  * Bootstrap Graph class
27526 > Prameters
27527  -sm {number} sm 4
27528  -md {number} md 5
27529  @cfg {String} graphtype  bar | vbar | pie
27530  @cfg {number} g_x coodinator | centre x (pie)
27531  @cfg {number} g_y coodinator | centre y (pie)
27532  @cfg {number} g_r radius (pie)
27533  @cfg {number} g_height height of the chart (respected by all elements in the set)
27534  @cfg {number} g_width width of the chart (respected by all elements in the set)
27535  @cfg {Object} title The title of the chart
27536     
27537  -{Array}  values
27538  -opts (object) options for the chart 
27539      o {
27540      o type (string) type of endings of the bar. Default: 'square'. Other options are: 'round', 'sharp', 'soft'.
27541      o gutter (number)(string) default '20%' (WHAT DOES IT DO?)
27542      o vgutter (number)
27543      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.
27544      o stacked (boolean) whether or not to tread values as in a stacked bar chart
27545      o to
27546      o stretch (boolean)
27547      o }
27548  -opts (object) options for the pie
27549      o{
27550      o cut
27551      o startAngle (number)
27552      o endAngle (number)
27553      } 
27554  *
27555  * @constructor
27556  * Create a new Input
27557  * @param {Object} config The config object
27558  */
27559
27560 Roo.bootstrap.Graph = function(config){
27561     Roo.bootstrap.Graph.superclass.constructor.call(this, config);
27562     
27563     this.addEvents({
27564         // img events
27565         /**
27566          * @event click
27567          * The img click event for the img.
27568          * @param {Roo.EventObject} e
27569          */
27570         "click" : true
27571     });
27572 };
27573
27574 Roo.extend(Roo.bootstrap.Graph, Roo.bootstrap.Component,  {
27575     
27576     sm: 4,
27577     md: 5,
27578     graphtype: 'bar',
27579     g_height: 250,
27580     g_width: 400,
27581     g_x: 50,
27582     g_y: 50,
27583     g_r: 30,
27584     opts:{
27585         //g_colors: this.colors,
27586         g_type: 'soft',
27587         g_gutter: '20%'
27588
27589     },
27590     title : false,
27591
27592     getAutoCreate : function(){
27593         
27594         var cfg = {
27595             tag: 'div',
27596             html : null
27597         };
27598         
27599         
27600         return  cfg;
27601     },
27602
27603     onRender : function(ct,position){
27604         
27605         
27606         Roo.bootstrap.Graph.superclass.onRender.call(this,ct,position);
27607         
27608         if (typeof(Raphael) == 'undefined') {
27609             Roo.bootstrap.MessageBox.alert("Error","Raphael is not availabe");
27610             return;
27611         }
27612         
27613         this.raphael = Raphael(this.el.dom);
27614         
27615                     // data1 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27616                     // data2 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27617                     // data3 = [[55, 20, 13, 32, 5, 1, 2, 10], [10, 2, 1, 5, 32, 13, 20, 55], [12, 20, 30]],
27618                     // txtattr = { font: "12px 'Fontin Sans', Fontin-Sans, sans-serif" };
27619                 /*
27620                 r.text(160, 10, "Single Series Chart").attr(txtattr);
27621                 r.text(480, 10, "Multiline Series Chart").attr(txtattr);
27622                 r.text(160, 250, "Multiple Series Stacked Chart").attr(txtattr);
27623                 r.text(480, 250, 'Multiline Series Stacked Vertical Chart. Type "round"').attr(txtattr);
27624                 
27625                 r.barchart(10, 10, 300, 220, [[55, 20, 13, 32, 5, 1, 2, 10]], 0, {type: "sharp"});
27626                 r.barchart(330, 10, 300, 220, data1);
27627                 r.barchart(10, 250, 300, 220, data2, {stacked: true});
27628                 r.barchart(330, 250, 300, 220, data3, {stacked: true, type: "round"});
27629                 */
27630                 
27631                 // var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27632                 // r.barchart(30, 30, 560, 250,  xdata, {
27633                 //    labels : [55, 20, 13, 32, 5, 1, 2, 10,5 , 10],
27634                 //     axis : "0 0 1 1",
27635                 //     axisxlabels :  xdata
27636                 //     //yvalues : cols,
27637                    
27638                 // });
27639 //        var xdata = [55, 20, 13, 32, 5, 1, 2, 10,5 , 10];
27640 //        
27641 //        this.load(null,xdata,{
27642 //                axis : "0 0 1 1",
27643 //                axisxlabels :  xdata
27644 //                });
27645
27646     },
27647
27648     load : function(graphtype,xdata,opts)
27649     {
27650         this.raphael.clear();
27651         if(!graphtype) {
27652             graphtype = this.graphtype;
27653         }
27654         if(!opts){
27655             opts = this.opts;
27656         }
27657         var r = this.raphael,
27658             fin = function () {
27659                 this.flag = r.popup(this.bar.x, this.bar.y, this.bar.value || "0").insertBefore(this);
27660             },
27661             fout = function () {
27662                 this.flag.animate({opacity: 0}, 300, function () {this.remove();});
27663             },
27664             pfin = function() {
27665                 this.sector.stop();
27666                 this.sector.scale(1.1, 1.1, this.cx, this.cy);
27667
27668                 if (this.label) {
27669                     this.label[0].stop();
27670                     this.label[0].attr({ r: 7.5 });
27671                     this.label[1].attr({ "font-weight": 800 });
27672                 }
27673             },
27674             pfout = function() {
27675                 this.sector.animate({ transform: 's1 1 ' + this.cx + ' ' + this.cy }, 500, "bounce");
27676
27677                 if (this.label) {
27678                     this.label[0].animate({ r: 5 }, 500, "bounce");
27679                     this.label[1].attr({ "font-weight": 400 });
27680                 }
27681             };
27682
27683         switch(graphtype){
27684             case 'bar':
27685                 this.raphael.barchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27686                 break;
27687             case 'hbar':
27688                 this.raphael.hbarchart(this.g_x,this.g_y,this.g_width,this.g_height,xdata,opts).hover(fin,fout);
27689                 break;
27690             case 'pie':
27691 //                opts = { legend: ["%% - Enterprise Users", "% - ddd","Chrome Users"], legendpos: "west", 
27692 //                href: ["http://raphaeljs.com", "http://g.raphaeljs.com"]};
27693 //            
27694                 this.raphael.piechart(this.g_x,this.g_y,this.g_r,xdata,opts).hover(pfin, pfout);
27695                 
27696                 break;
27697
27698         }
27699         
27700         if(this.title){
27701             this.raphael.text(this.title.x, this.title.y, this.title.text).attr(this.title.attr);
27702         }
27703         
27704     },
27705     
27706     setTitle: function(o)
27707     {
27708         this.title = o;
27709     },
27710     
27711     initEvents: function() {
27712         
27713         if(!this.href){
27714             this.el.on('click', this.onClick, this);
27715         }
27716     },
27717     
27718     onClick : function(e)
27719     {
27720         Roo.log('img onclick');
27721         this.fireEvent('click', this, e);
27722     }
27723    
27724 });
27725
27726  
27727 /*
27728  * - LGPL
27729  *
27730  * numberBox
27731  * 
27732  */
27733 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27734
27735 /**
27736  * @class Roo.bootstrap.dash.NumberBox
27737  * @extends Roo.bootstrap.Component
27738  * Bootstrap NumberBox class
27739  * @cfg {String} headline Box headline
27740  * @cfg {String} content Box content
27741  * @cfg {String} icon Box icon
27742  * @cfg {String} footer Footer text
27743  * @cfg {String} fhref Footer href
27744  * 
27745  * @constructor
27746  * Create a new NumberBox
27747  * @param {Object} config The config object
27748  */
27749
27750
27751 Roo.bootstrap.dash.NumberBox = function(config){
27752     Roo.bootstrap.dash.NumberBox.superclass.constructor.call(this, config);
27753     
27754 };
27755
27756 Roo.extend(Roo.bootstrap.dash.NumberBox, Roo.bootstrap.Component,  {
27757     
27758     headline : '',
27759     content : '',
27760     icon : '',
27761     footer : '',
27762     fhref : '',
27763     ficon : '',
27764     
27765     getAutoCreate : function(){
27766         
27767         var cfg = {
27768             tag : 'div',
27769             cls : 'small-box ',
27770             cn : [
27771                 {
27772                     tag : 'div',
27773                     cls : 'inner',
27774                     cn :[
27775                         {
27776                             tag : 'h3',
27777                             cls : 'roo-headline',
27778                             html : this.headline
27779                         },
27780                         {
27781                             tag : 'p',
27782                             cls : 'roo-content',
27783                             html : this.content
27784                         }
27785                     ]
27786                 }
27787             ]
27788         };
27789         
27790         if(this.icon){
27791             cfg.cn.push({
27792                 tag : 'div',
27793                 cls : 'icon',
27794                 cn :[
27795                     {
27796                         tag : 'i',
27797                         cls : 'ion ' + this.icon
27798                     }
27799                 ]
27800             });
27801         }
27802         
27803         if(this.footer){
27804             var footer = {
27805                 tag : 'a',
27806                 cls : 'small-box-footer',
27807                 href : this.fhref || '#',
27808                 html : this.footer
27809             };
27810             
27811             cfg.cn.push(footer);
27812             
27813         }
27814         
27815         return  cfg;
27816     },
27817
27818     onRender : function(ct,position){
27819         Roo.bootstrap.dash.NumberBox.superclass.onRender.call(this,ct,position);
27820
27821
27822        
27823                 
27824     },
27825
27826     setHeadline: function (value)
27827     {
27828         this.el.select('.roo-headline',true).first().dom.innerHTML = value;
27829     },
27830     
27831     setFooter: function (value, href)
27832     {
27833         this.el.select('a.small-box-footer',true).first().dom.innerHTML = value;
27834         
27835         if(href){
27836             this.el.select('a.small-box-footer',true).first().attr('href', href);
27837         }
27838         
27839     },
27840
27841     setContent: function (value)
27842     {
27843         this.el.select('.roo-content',true).first().dom.innerHTML = value;
27844     },
27845
27846     initEvents: function() 
27847     {   
27848         
27849     }
27850     
27851 });
27852
27853  
27854 /*
27855  * - LGPL
27856  *
27857  * TabBox
27858  * 
27859  */
27860 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
27861
27862 /**
27863  * @class Roo.bootstrap.dash.TabBox
27864  * @extends Roo.bootstrap.Component
27865  * Bootstrap TabBox class
27866  * @cfg {String} title Title of the TabBox
27867  * @cfg {String} icon Icon of the TabBox
27868  * @cfg {Boolean} showtabs (true|false) show the tabs default true
27869  * @cfg {Boolean} tabScrollable (true|false) tab scrollable when mobile view default false
27870  * 
27871  * @constructor
27872  * Create a new TabBox
27873  * @param {Object} config The config object
27874  */
27875
27876
27877 Roo.bootstrap.dash.TabBox = function(config){
27878     Roo.bootstrap.dash.TabBox.superclass.constructor.call(this, config);
27879     this.addEvents({
27880         // raw events
27881         /**
27882          * @event addpane
27883          * When a pane is added
27884          * @param {Roo.bootstrap.dash.TabPane} pane
27885          */
27886         "addpane" : true,
27887         /**
27888          * @event activatepane
27889          * When a pane is activated
27890          * @param {Roo.bootstrap.dash.TabPane} pane
27891          */
27892         "activatepane" : true
27893         
27894          
27895     });
27896     
27897     this.panes = [];
27898 };
27899
27900 Roo.extend(Roo.bootstrap.dash.TabBox, Roo.bootstrap.Component,  {
27901
27902     title : '',
27903     icon : false,
27904     showtabs : true,
27905     tabScrollable : false,
27906     
27907     getChildContainer : function()
27908     {
27909         return this.el.select('.tab-content', true).first();
27910     },
27911     
27912     getAutoCreate : function(){
27913         
27914         var header = {
27915             tag: 'li',
27916             cls: 'pull-left header',
27917             html: this.title,
27918             cn : []
27919         };
27920         
27921         if(this.icon){
27922             header.cn.push({
27923                 tag: 'i',
27924                 cls: 'fa ' + this.icon
27925             });
27926         }
27927         
27928         var h = {
27929             tag: 'ul',
27930             cls: 'nav nav-tabs pull-right',
27931             cn: [
27932                 header
27933             ]
27934         };
27935         
27936         if(this.tabScrollable){
27937             h = {
27938                 tag: 'div',
27939                 cls: 'tab-header',
27940                 cn: [
27941                     {
27942                         tag: 'ul',
27943                         cls: 'nav nav-tabs pull-right',
27944                         cn: [
27945                             header
27946                         ]
27947                     }
27948                 ]
27949             };
27950         }
27951         
27952         var cfg = {
27953             tag: 'div',
27954             cls: 'nav-tabs-custom',
27955             cn: [
27956                 h,
27957                 {
27958                     tag: 'div',
27959                     cls: 'tab-content no-padding',
27960                     cn: []
27961                 }
27962             ]
27963         };
27964
27965         return  cfg;
27966     },
27967     initEvents : function()
27968     {
27969         //Roo.log('add add pane handler');
27970         this.on('addpane', this.onAddPane, this);
27971     },
27972      /**
27973      * Updates the box title
27974      * @param {String} html to set the title to.
27975      */
27976     setTitle : function(value)
27977     {
27978         this.el.select('.nav-tabs .header', true).first().dom.innerHTML = value;
27979     },
27980     onAddPane : function(pane)
27981     {
27982         this.panes.push(pane);
27983         //Roo.log('addpane');
27984         //Roo.log(pane);
27985         // tabs are rendere left to right..
27986         if(!this.showtabs){
27987             return;
27988         }
27989         
27990         var ctr = this.el.select('.nav-tabs', true).first();
27991          
27992          
27993         var existing = ctr.select('.nav-tab',true);
27994         var qty = existing.getCount();;
27995         
27996         
27997         var tab = ctr.createChild({
27998             tag : 'li',
27999             cls : 'nav-tab' + (qty ? '' : ' active'),
28000             cn : [
28001                 {
28002                     tag : 'a',
28003                     href:'#',
28004                     html : pane.title
28005                 }
28006             ]
28007         }, qty ? existing.first().dom : ctr.select('.header', true).first().dom );
28008         pane.tab = tab;
28009         
28010         tab.on('click', this.onTabClick.createDelegate(this, [pane], true));
28011         if (!qty) {
28012             pane.el.addClass('active');
28013         }
28014         
28015                 
28016     },
28017     onTabClick : function(ev,un,ob,pane)
28018     {
28019         //Roo.log('tab - prev default');
28020         ev.preventDefault();
28021         
28022         
28023         this.el.select('.nav-tabs li.nav-tab', true).removeClass('active');
28024         pane.tab.addClass('active');
28025         //Roo.log(pane.title);
28026         this.getChildContainer().select('.tab-pane',true).removeClass('active');
28027         // technically we should have a deactivate event.. but maybe add later.
28028         // and it should not de-activate the selected tab...
28029         this.fireEvent('activatepane', pane);
28030         pane.el.addClass('active');
28031         pane.fireEvent('activate');
28032         
28033         
28034     },
28035     
28036     getActivePane : function()
28037     {
28038         var r = false;
28039         Roo.each(this.panes, function(p) {
28040             if(p.el.hasClass('active')){
28041                 r = p;
28042                 return false;
28043             }
28044             
28045             return;
28046         });
28047         
28048         return r;
28049     }
28050     
28051     
28052 });
28053
28054  
28055 /*
28056  * - LGPL
28057  *
28058  * Tab pane
28059  * 
28060  */
28061 Roo.bootstrap.dash = Roo.bootstrap.dash || {};
28062 /**
28063  * @class Roo.bootstrap.TabPane
28064  * @extends Roo.bootstrap.Component
28065  * Bootstrap TabPane class
28066  * @cfg {Boolean} active (false | true) Default false
28067  * @cfg {String} title title of panel
28068
28069  * 
28070  * @constructor
28071  * Create a new TabPane
28072  * @param {Object} config The config object
28073  */
28074
28075 Roo.bootstrap.dash.TabPane = function(config){
28076     Roo.bootstrap.dash.TabPane.superclass.constructor.call(this, config);
28077     
28078     this.addEvents({
28079         // raw events
28080         /**
28081          * @event activate
28082          * When a pane is activated
28083          * @param {Roo.bootstrap.dash.TabPane} pane
28084          */
28085         "activate" : true
28086          
28087     });
28088 };
28089
28090 Roo.extend(Roo.bootstrap.dash.TabPane, Roo.bootstrap.Component,  {
28091     
28092     active : false,
28093     title : '',
28094     
28095     // the tabBox that this is attached to.
28096     tab : false,
28097      
28098     getAutoCreate : function() 
28099     {
28100         var cfg = {
28101             tag: 'div',
28102             cls: 'tab-pane'
28103         };
28104         
28105         if(this.active){
28106             cfg.cls += ' active';
28107         }
28108         
28109         return cfg;
28110     },
28111     initEvents  : function()
28112     {
28113         //Roo.log('trigger add pane handler');
28114         this.parent().fireEvent('addpane', this)
28115     },
28116     
28117      /**
28118      * Updates the tab title 
28119      * @param {String} html to set the title to.
28120      */
28121     setTitle: function(str)
28122     {
28123         if (!this.tab) {
28124             return;
28125         }
28126         this.title = str;
28127         this.tab.select('a', true).first().dom.innerHTML = str;
28128         
28129     }
28130     
28131     
28132     
28133 });
28134
28135  
28136
28137
28138  /*
28139  * - LGPL
28140  *
28141  * menu
28142  * 
28143  */
28144 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28145
28146 /**
28147  * @class Roo.bootstrap.menu.Menu
28148  * @extends Roo.bootstrap.Component
28149  * Bootstrap Menu class - container for Menu
28150  * @cfg {String} html Text of the menu
28151  * @cfg {String} weight (default | primary | success | info | warning | danger | inverse)
28152  * @cfg {String} icon Font awesome icon
28153  * @cfg {String} pos Menu align to (top | bottom) default bottom
28154  * 
28155  * 
28156  * @constructor
28157  * Create a new Menu
28158  * @param {Object} config The config object
28159  */
28160
28161
28162 Roo.bootstrap.menu.Menu = function(config){
28163     Roo.bootstrap.menu.Menu.superclass.constructor.call(this, config);
28164     
28165     this.addEvents({
28166         /**
28167          * @event beforeshow
28168          * Fires before this menu is displayed
28169          * @param {Roo.bootstrap.menu.Menu} this
28170          */
28171         beforeshow : true,
28172         /**
28173          * @event beforehide
28174          * Fires before this menu is hidden
28175          * @param {Roo.bootstrap.menu.Menu} this
28176          */
28177         beforehide : true,
28178         /**
28179          * @event show
28180          * Fires after this menu is displayed
28181          * @param {Roo.bootstrap.menu.Menu} this
28182          */
28183         show : true,
28184         /**
28185          * @event hide
28186          * Fires after this menu is hidden
28187          * @param {Roo.bootstrap.menu.Menu} this
28188          */
28189         hide : true,
28190         /**
28191          * @event click
28192          * Fires when this menu is clicked (or when the enter key is pressed while it is active)
28193          * @param {Roo.bootstrap.menu.Menu} this
28194          * @param {Roo.EventObject} e
28195          */
28196         click : true
28197     });
28198     
28199 };
28200
28201 Roo.extend(Roo.bootstrap.menu.Menu, Roo.bootstrap.Component,  {
28202     
28203     submenu : false,
28204     html : '',
28205     weight : 'default',
28206     icon : false,
28207     pos : 'bottom',
28208     
28209     
28210     getChildContainer : function() {
28211         if(this.isSubMenu){
28212             return this.el;
28213         }
28214         
28215         return this.el.select('ul.dropdown-menu', true).first();  
28216     },
28217     
28218     getAutoCreate : function()
28219     {
28220         var text = [
28221             {
28222                 tag : 'span',
28223                 cls : 'roo-menu-text',
28224                 html : this.html
28225             }
28226         ];
28227         
28228         if(this.icon){
28229             text.unshift({
28230                 tag : 'i',
28231                 cls : 'fa ' + this.icon
28232             })
28233         }
28234         
28235         
28236         var cfg = {
28237             tag : 'div',
28238             cls : 'btn-group',
28239             cn : [
28240                 {
28241                     tag : 'button',
28242                     cls : 'dropdown-button btn btn-' + this.weight,
28243                     cn : text
28244                 },
28245                 {
28246                     tag : 'button',
28247                     cls : 'dropdown-toggle btn btn-' + this.weight,
28248                     cn : [
28249                         {
28250                             tag : 'span',
28251                             cls : 'caret'
28252                         }
28253                     ]
28254                 },
28255                 {
28256                     tag : 'ul',
28257                     cls : 'dropdown-menu'
28258                 }
28259             ]
28260             
28261         };
28262         
28263         if(this.pos == 'top'){
28264             cfg.cls += ' dropup';
28265         }
28266         
28267         if(this.isSubMenu){
28268             cfg = {
28269                 tag : 'ul',
28270                 cls : 'dropdown-menu'
28271             }
28272         }
28273         
28274         return cfg;
28275     },
28276     
28277     onRender : function(ct, position)
28278     {
28279         this.isSubMenu = ct.hasClass('dropdown-submenu');
28280         
28281         Roo.bootstrap.menu.Menu.superclass.onRender.call(this, ct, position);
28282     },
28283     
28284     initEvents : function() 
28285     {
28286         if(this.isSubMenu){
28287             return;
28288         }
28289         
28290         this.hidden = true;
28291         
28292         this.triggerEl = this.el.select('button.dropdown-toggle', true).first();
28293         this.triggerEl.on('click', this.onTriggerPress, this);
28294         
28295         this.buttonEl = this.el.select('button.dropdown-button', true).first();
28296         this.buttonEl.on('click', this.onClick, this);
28297         
28298     },
28299     
28300     list : function()
28301     {
28302         if(this.isSubMenu){
28303             return this.el;
28304         }
28305         
28306         return this.el.select('ul.dropdown-menu', true).first();
28307     },
28308     
28309     onClick : function(e)
28310     {
28311         this.fireEvent("click", this, e);
28312     },
28313     
28314     onTriggerPress  : function(e)
28315     {   
28316         if (this.isVisible()) {
28317             this.hide();
28318         } else {
28319             this.show();
28320         }
28321     },
28322     
28323     isVisible : function(){
28324         return !this.hidden;
28325     },
28326     
28327     show : function()
28328     {
28329         this.fireEvent("beforeshow", this);
28330         
28331         this.hidden = false;
28332         this.el.addClass('open');
28333         
28334         Roo.get(document).on("mouseup", this.onMouseUp, this);
28335         
28336         this.fireEvent("show", this);
28337         
28338         
28339     },
28340     
28341     hide : function()
28342     {
28343         this.fireEvent("beforehide", this);
28344         
28345         this.hidden = true;
28346         this.el.removeClass('open');
28347         
28348         Roo.get(document).un("mouseup", this.onMouseUp);
28349         
28350         this.fireEvent("hide", this);
28351     },
28352     
28353     onMouseUp : function()
28354     {
28355         this.hide();
28356     }
28357     
28358 });
28359
28360  
28361  /*
28362  * - LGPL
28363  *
28364  * menu item
28365  * 
28366  */
28367 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28368
28369 /**
28370  * @class Roo.bootstrap.menu.Item
28371  * @extends Roo.bootstrap.Component
28372  * Bootstrap MenuItem class
28373  * @cfg {Boolean} submenu (true | false) default false
28374  * @cfg {String} html text of the item
28375  * @cfg {String} href the link
28376  * @cfg {Boolean} disable (true | false) default false
28377  * @cfg {Boolean} preventDefault (true | false) default true
28378  * @cfg {String} icon Font awesome icon
28379  * @cfg {String} pos Submenu align to (left | right) default right 
28380  * 
28381  * 
28382  * @constructor
28383  * Create a new Item
28384  * @param {Object} config The config object
28385  */
28386
28387
28388 Roo.bootstrap.menu.Item = function(config){
28389     Roo.bootstrap.menu.Item.superclass.constructor.call(this, config);
28390     this.addEvents({
28391         /**
28392          * @event mouseover
28393          * Fires when the mouse is hovering over this menu
28394          * @param {Roo.bootstrap.menu.Item} this
28395          * @param {Roo.EventObject} e
28396          */
28397         mouseover : true,
28398         /**
28399          * @event mouseout
28400          * Fires when the mouse exits this menu
28401          * @param {Roo.bootstrap.menu.Item} this
28402          * @param {Roo.EventObject} e
28403          */
28404         mouseout : true,
28405         // raw events
28406         /**
28407          * @event click
28408          * The raw click event for the entire grid.
28409          * @param {Roo.EventObject} e
28410          */
28411         click : true
28412     });
28413 };
28414
28415 Roo.extend(Roo.bootstrap.menu.Item, Roo.bootstrap.Component,  {
28416     
28417     submenu : false,
28418     href : '',
28419     html : '',
28420     preventDefault: true,
28421     disable : false,
28422     icon : false,
28423     pos : 'right',
28424     
28425     getAutoCreate : function()
28426     {
28427         var text = [
28428             {
28429                 tag : 'span',
28430                 cls : 'roo-menu-item-text',
28431                 html : this.html
28432             }
28433         ];
28434         
28435         if(this.icon){
28436             text.unshift({
28437                 tag : 'i',
28438                 cls : 'fa ' + this.icon
28439             })
28440         }
28441         
28442         var cfg = {
28443             tag : 'li',
28444             cn : [
28445                 {
28446                     tag : 'a',
28447                     href : this.href || '#',
28448                     cn : text
28449                 }
28450             ]
28451         };
28452         
28453         if(this.disable){
28454             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'disabled' : (cfg.cls + ' disabled');
28455         }
28456         
28457         if(this.submenu){
28458             cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'dropdown-submenu' : (cfg.cls + ' dropdown-submenu');
28459             
28460             if(this.pos == 'left'){
28461                 cfg.cls = (typeof(cfg.cls) == 'undefined') ? 'pull-left' : (cfg.cls + ' pull-left');
28462             }
28463         }
28464         
28465         return cfg;
28466     },
28467     
28468     initEvents : function() 
28469     {
28470         this.el.on('mouseover', this.onMouseOver, this);
28471         this.el.on('mouseout', this.onMouseOut, this);
28472         
28473         this.el.select('a', true).first().on('click', this.onClick, this);
28474         
28475     },
28476     
28477     onClick : function(e)
28478     {
28479         if(this.preventDefault){
28480             e.preventDefault();
28481         }
28482         
28483         this.fireEvent("click", this, e);
28484     },
28485     
28486     onMouseOver : function(e)
28487     {
28488         if(this.submenu && this.pos == 'left'){
28489             this.el.select('ul.dropdown-menu', true).first().setLeft(this.el.select('ul.dropdown-menu', true).first().getWidth() * -1);
28490         }
28491         
28492         this.fireEvent("mouseover", this, e);
28493     },
28494     
28495     onMouseOut : function(e)
28496     {
28497         this.fireEvent("mouseout", this, e);
28498     }
28499 });
28500
28501  
28502
28503  /*
28504  * - LGPL
28505  *
28506  * menu separator
28507  * 
28508  */
28509 Roo.bootstrap.menu = Roo.bootstrap.menu || {};
28510
28511 /**
28512  * @class Roo.bootstrap.menu.Separator
28513  * @extends Roo.bootstrap.Component
28514  * Bootstrap Separator class
28515  * 
28516  * @constructor
28517  * Create a new Separator
28518  * @param {Object} config The config object
28519  */
28520
28521
28522 Roo.bootstrap.menu.Separator = function(config){
28523     Roo.bootstrap.menu.Separator.superclass.constructor.call(this, config);
28524 };
28525
28526 Roo.extend(Roo.bootstrap.menu.Separator, Roo.bootstrap.Component,  {
28527     
28528     getAutoCreate : function(){
28529         var cfg = {
28530             tag : 'li',
28531             cls: 'divider'
28532         };
28533         
28534         return cfg;
28535     }
28536    
28537 });
28538
28539  
28540
28541  /*
28542  * - LGPL
28543  *
28544  * Tooltip
28545  * 
28546  */
28547
28548 /**
28549  * @class Roo.bootstrap.Tooltip
28550  * Bootstrap Tooltip class
28551  * This is basic at present - all componets support it by default, however they should add tooltipEl() method
28552  * to determine which dom element triggers the tooltip.
28553  * 
28554  * It needs to add support for additional attributes like tooltip-position
28555  * 
28556  * @constructor
28557  * Create a new Toolti
28558  * @param {Object} config The config object
28559  */
28560
28561 Roo.bootstrap.Tooltip = function(config){
28562     Roo.bootstrap.Tooltip.superclass.constructor.call(this, config);
28563     
28564     this.alignment = Roo.bootstrap.Tooltip.alignment;
28565     
28566     if(typeof(config) != 'undefined' && typeof(config.alignment) != 'undefined'){
28567         this.alignment = config.alignment;
28568     }
28569     
28570 };
28571
28572 Roo.apply(Roo.bootstrap.Tooltip, {
28573     /**
28574      * @function init initialize tooltip monitoring.
28575      * @static
28576      */
28577     currentEl : false,
28578     currentTip : false,
28579     currentRegion : false,
28580     
28581     //  init : delay?
28582     
28583     init : function()
28584     {
28585         Roo.get(document).on('mouseover', this.enter ,this);
28586         Roo.get(document).on('mouseout', this.leave, this);
28587          
28588         
28589         this.currentTip = new Roo.bootstrap.Tooltip();
28590     },
28591     
28592     enter : function(ev)
28593     {
28594         var dom = ev.getTarget();
28595         
28596         //Roo.log(['enter',dom]);
28597         var el = Roo.fly(dom);
28598         if (this.currentEl) {
28599             //Roo.log(dom);
28600             //Roo.log(this.currentEl);
28601             //Roo.log(this.currentEl.contains(dom));
28602             if (this.currentEl == el) {
28603                 return;
28604             }
28605             if (dom != this.currentEl.dom && this.currentEl.contains(dom)) {
28606                 return;
28607             }
28608
28609         }
28610         
28611         if (this.currentTip.el) {
28612             this.currentTip.el.setVisibilityMode(Roo.Element.DISPLAY).hide(); // force hiding...
28613         }    
28614         //Roo.log(ev);
28615         
28616         if(!el || el.dom == document){
28617             return;
28618         }
28619         
28620         var bindEl = el;
28621         
28622         // you can not look for children, as if el is the body.. then everythign is the child..
28623         if (!el.attr('tooltip')) { //
28624             if (!el.select("[tooltip]").elements.length) {
28625                 return;
28626             }
28627             // is the mouse over this child...?
28628             bindEl = el.select("[tooltip]").first();
28629             var xy = ev.getXY();
28630             if (!bindEl.getRegion().contains( { top : xy[1] ,right : xy[0] , bottom : xy[1], left : xy[0]})) {
28631                 //Roo.log("not in region.");
28632                 return;
28633             }
28634             //Roo.log("child element over..");
28635             
28636         }
28637         this.currentEl = bindEl;
28638         this.currentTip.bind(bindEl);
28639         this.currentRegion = Roo.lib.Region.getRegion(dom);
28640         this.currentTip.enter();
28641         
28642     },
28643     leave : function(ev)
28644     {
28645         var dom = ev.getTarget();
28646         //Roo.log(['leave',dom]);
28647         if (!this.currentEl) {
28648             return;
28649         }
28650         
28651         
28652         if (dom != this.currentEl.dom) {
28653             return;
28654         }
28655         var xy = ev.getXY();
28656         if (this.currentRegion.contains( new Roo.lib.Region( xy[1], xy[0] ,xy[1], xy[0]  ))) {
28657             return;
28658         }
28659         // only activate leave if mouse cursor is outside... bounding box..
28660         
28661         
28662         
28663         
28664         if (this.currentTip) {
28665             this.currentTip.leave();
28666         }
28667         //Roo.log('clear currentEl');
28668         this.currentEl = false;
28669         
28670         
28671     },
28672     alignment : {
28673         'left' : ['r-l', [-2,0], 'right'],
28674         'right' : ['l-r', [2,0], 'left'],
28675         'bottom' : ['t-b', [0,2], 'top'],
28676         'top' : [ 'b-t', [0,-2], 'bottom']
28677     }
28678     
28679 });
28680
28681
28682 Roo.extend(Roo.bootstrap.Tooltip, Roo.bootstrap.Component,  {
28683     
28684     
28685     bindEl : false,
28686     
28687     delay : null, // can be { show : 300 , hide: 500}
28688     
28689     timeout : null,
28690     
28691     hoverState : null, //???
28692     
28693     placement : 'bottom', 
28694     
28695     alignment : false,
28696     
28697     getAutoCreate : function(){
28698     
28699         var cfg = {
28700            cls : 'tooltip',   
28701            role : 'tooltip',
28702            cn : [
28703                 {
28704                     cls : 'tooltip-arrow arrow'
28705                 },
28706                 {
28707                     cls : 'tooltip-inner'
28708                 }
28709            ]
28710         };
28711         
28712         return cfg;
28713     },
28714     bind : function(el)
28715     {
28716         this.bindEl = el;
28717     },
28718     
28719     initEvents : function()
28720     {
28721         this.arrowEl = this.el.select('.arrow', true).first();
28722         this.innerEl = this.el.select('.tooltip-inner', true).first();
28723     },
28724     
28725     enter : function () {
28726        
28727         if (this.timeout != null) {
28728             clearTimeout(this.timeout);
28729         }
28730         
28731         this.hoverState = 'in';
28732          //Roo.log("enter - show");
28733         if (!this.delay || !this.delay.show) {
28734             this.show();
28735             return;
28736         }
28737         var _t = this;
28738         this.timeout = setTimeout(function () {
28739             if (_t.hoverState == 'in') {
28740                 _t.show();
28741             }
28742         }, this.delay.show);
28743     },
28744     leave : function()
28745     {
28746         clearTimeout(this.timeout);
28747     
28748         this.hoverState = 'out';
28749          if (!this.delay || !this.delay.hide) {
28750             this.hide();
28751             return;
28752         }
28753        
28754         var _t = this;
28755         this.timeout = setTimeout(function () {
28756             //Roo.log("leave - timeout");
28757             
28758             if (_t.hoverState == 'out') {
28759                 _t.hide();
28760                 Roo.bootstrap.Tooltip.currentEl = false;
28761             }
28762         }, delay);
28763     },
28764     
28765     show : function (msg)
28766     {
28767         if (!this.el) {
28768             this.render(document.body);
28769         }
28770         // set content.
28771         //Roo.log([this.bindEl, this.bindEl.attr('tooltip')]);
28772         
28773         var tip = msg || this.bindEl.attr('tooltip') || this.bindEl.select("[tooltip]").first().attr('tooltip');
28774         
28775         this.el.select('.tooltip-inner',true).first().dom.innerHTML = tip;
28776         
28777         this.el.removeClass(['fade','top','bottom', 'left', 'right','in',
28778                              'bs-tooltip-top','bs-tooltip-bottom', 'bs-tooltip-left', 'bs-tooltip-right']);
28779         
28780         var placement = typeof this.placement == 'function' ?
28781             this.placement.call(this, this.el, on_el) :
28782             this.placement;
28783             
28784         var autoToken = /\s?auto?\s?/i;
28785         var autoPlace = autoToken.test(placement);
28786         if (autoPlace) {
28787             placement = placement.replace(autoToken, '') || 'top';
28788         }
28789         
28790         //this.el.detach()
28791         //this.el.setXY([0,0]);
28792         this.el.show();
28793         //this.el.dom.style.display='block';
28794         
28795         //this.el.appendTo(on_el);
28796         
28797         var p = this.getPosition();
28798         var box = this.el.getBox();
28799         
28800         if (autoPlace) {
28801             // fixme..
28802         }
28803         
28804         var align = this.alignment[placement];
28805         
28806         var xy = this.el.getAlignToXY(this.bindEl, align[0], align[1]);
28807         
28808         if(placement == 'top' || placement == 'bottom'){
28809             if(xy[0] < 0){
28810                 placement = 'right';
28811             }
28812             
28813             if(xy[0] + this.el.getWidth() > Roo.lib.Dom.getViewWidth()){
28814                 placement = 'left';
28815             }
28816             
28817             var scroll = Roo.select('body', true).first().getScroll();
28818             
28819             if(xy[1] > Roo.lib.Dom.getViewHeight() + scroll.top - this.el.getHeight()){
28820                 placement = 'top';
28821             }
28822             
28823             align = this.alignment[placement];
28824             
28825             this.arrowEl.setLeft((this.innerEl.getWidth()/2) - 5);
28826             
28827         }
28828         
28829         this.el.alignTo(this.bindEl, align[0],align[1]);
28830         //var arrow = this.el.select('.arrow',true).first();
28831         //arrow.set(align[2], 
28832         
28833         this.el.addClass(placement);
28834         this.el.addClass("bs-tooltip-"+ placement);
28835         
28836         this.el.addClass('in fade show');
28837         
28838         this.hoverState = null;
28839         
28840         if (this.el.hasClass('fade')) {
28841             // fade it?
28842         }
28843         
28844         
28845         
28846         
28847         
28848     },
28849     hide : function()
28850     {
28851          
28852         if (!this.el) {
28853             return;
28854         }
28855         //this.el.setXY([0,0]);
28856         this.el.removeClass(['show', 'in']);
28857         //this.el.hide();
28858         
28859     }
28860     
28861 });
28862  
28863
28864  /*
28865  * - LGPL
28866  *
28867  * Location Picker
28868  * 
28869  */
28870
28871 /**
28872  * @class Roo.bootstrap.LocationPicker
28873  * @extends Roo.bootstrap.Component
28874  * Bootstrap LocationPicker class
28875  * @cfg {Number} latitude Position when init default 0
28876  * @cfg {Number} longitude Position when init default 0
28877  * @cfg {Number} zoom default 15
28878  * @cfg {String} mapTypeId default google.maps.MapTypeId.ROADMAP
28879  * @cfg {Boolean} mapTypeControl default false
28880  * @cfg {Boolean} disableDoubleClickZoom default false
28881  * @cfg {Boolean} scrollwheel default true
28882  * @cfg {Boolean} streetViewControl default false
28883  * @cfg {Number} radius default 0
28884  * @cfg {String} locationName
28885  * @cfg {Boolean} draggable default true
28886  * @cfg {Boolean} enableAutocomplete default false
28887  * @cfg {Boolean} enableReverseGeocode default true
28888  * @cfg {String} markerTitle
28889  * 
28890  * @constructor
28891  * Create a new LocationPicker
28892  * @param {Object} config The config object
28893  */
28894
28895
28896 Roo.bootstrap.LocationPicker = function(config){
28897     
28898     Roo.bootstrap.LocationPicker.superclass.constructor.call(this, config);
28899     
28900     this.addEvents({
28901         /**
28902          * @event initial
28903          * Fires when the picker initialized.
28904          * @param {Roo.bootstrap.LocationPicker} this
28905          * @param {Google Location} location
28906          */
28907         initial : true,
28908         /**
28909          * @event positionchanged
28910          * Fires when the picker position changed.
28911          * @param {Roo.bootstrap.LocationPicker} this
28912          * @param {Google Location} location
28913          */
28914         positionchanged : true,
28915         /**
28916          * @event resize
28917          * Fires when the map resize.
28918          * @param {Roo.bootstrap.LocationPicker} this
28919          */
28920         resize : true,
28921         /**
28922          * @event show
28923          * Fires when the map show.
28924          * @param {Roo.bootstrap.LocationPicker} this
28925          */
28926         show : true,
28927         /**
28928          * @event hide
28929          * Fires when the map hide.
28930          * @param {Roo.bootstrap.LocationPicker} this
28931          */
28932         hide : true,
28933         /**
28934          * @event mapClick
28935          * Fires when click the map.
28936          * @param {Roo.bootstrap.LocationPicker} this
28937          * @param {Map event} e
28938          */
28939         mapClick : true,
28940         /**
28941          * @event mapRightClick
28942          * Fires when right click the map.
28943          * @param {Roo.bootstrap.LocationPicker} this
28944          * @param {Map event} e
28945          */
28946         mapRightClick : true,
28947         /**
28948          * @event markerClick
28949          * Fires when click the marker.
28950          * @param {Roo.bootstrap.LocationPicker} this
28951          * @param {Map event} e
28952          */
28953         markerClick : true,
28954         /**
28955          * @event markerRightClick
28956          * Fires when right click the marker.
28957          * @param {Roo.bootstrap.LocationPicker} this
28958          * @param {Map event} e
28959          */
28960         markerRightClick : true,
28961         /**
28962          * @event OverlayViewDraw
28963          * Fires when OverlayView Draw
28964          * @param {Roo.bootstrap.LocationPicker} this
28965          */
28966         OverlayViewDraw : true,
28967         /**
28968          * @event OverlayViewOnAdd
28969          * Fires when OverlayView Draw
28970          * @param {Roo.bootstrap.LocationPicker} this
28971          */
28972         OverlayViewOnAdd : true,
28973         /**
28974          * @event OverlayViewOnRemove
28975          * Fires when OverlayView Draw
28976          * @param {Roo.bootstrap.LocationPicker} this
28977          */
28978         OverlayViewOnRemove : true,
28979         /**
28980          * @event OverlayViewShow
28981          * Fires when OverlayView Draw
28982          * @param {Roo.bootstrap.LocationPicker} this
28983          * @param {Pixel} cpx
28984          */
28985         OverlayViewShow : true,
28986         /**
28987          * @event OverlayViewHide
28988          * Fires when OverlayView Draw
28989          * @param {Roo.bootstrap.LocationPicker} this
28990          */
28991         OverlayViewHide : true,
28992         /**
28993          * @event loadexception
28994          * Fires when load google lib failed.
28995          * @param {Roo.bootstrap.LocationPicker} this
28996          */
28997         loadexception : true
28998     });
28999         
29000 };
29001
29002 Roo.extend(Roo.bootstrap.LocationPicker, Roo.bootstrap.Component,  {
29003     
29004     gMapContext: false,
29005     
29006     latitude: 0,
29007     longitude: 0,
29008     zoom: 15,
29009     mapTypeId: false,
29010     mapTypeControl: false,
29011     disableDoubleClickZoom: false,
29012     scrollwheel: true,
29013     streetViewControl: false,
29014     radius: 0,
29015     locationName: '',
29016     draggable: true,
29017     enableAutocomplete: false,
29018     enableReverseGeocode: true,
29019     markerTitle: '',
29020     
29021     getAutoCreate: function()
29022     {
29023
29024         var cfg = {
29025             tag: 'div',
29026             cls: 'roo-location-picker'
29027         };
29028         
29029         return cfg
29030     },
29031     
29032     initEvents: function(ct, position)
29033     {       
29034         if(!this.el.getWidth() || this.isApplied()){
29035             return;
29036         }
29037         
29038         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29039         
29040         this.initial();
29041     },
29042     
29043     initial: function()
29044     {
29045         if(typeof(google) == 'undefined' || typeof(google.maps) == 'undefined'){
29046             this.fireEvent('loadexception', this);
29047             return;
29048         }
29049         
29050         if(!this.mapTypeId){
29051             this.mapTypeId = google.maps.MapTypeId.ROADMAP;
29052         }
29053         
29054         this.gMapContext = this.GMapContext();
29055         
29056         this.initOverlayView();
29057         
29058         this.OverlayView = new Roo.bootstrap.LocationPicker.OverlayView(this.gMapContext.map);
29059         
29060         var _this = this;
29061                 
29062         google.maps.event.addListener(this.gMapContext.marker, "dragend", function(event) {
29063             _this.setPosition(_this.gMapContext.marker.position);
29064         });
29065         
29066         google.maps.event.addListener(this.gMapContext.map, 'click', function(event){
29067             _this.fireEvent('mapClick', this, event);
29068             
29069         });
29070
29071         google.maps.event.addListener(this.gMapContext.map, 'rightclick', function(event){
29072             _this.fireEvent('mapRightClick', this, event);
29073             
29074         });
29075         
29076         google.maps.event.addListener(this.gMapContext.marker, 'click', function(event){
29077             _this.fireEvent('markerClick', this, event);
29078             
29079         });
29080
29081         google.maps.event.addListener(this.gMapContext.marker, 'rightclick', function(event){
29082             _this.fireEvent('markerRightClick', this, event);
29083             
29084         });
29085         
29086         this.setPosition(this.gMapContext.location);
29087         
29088         this.fireEvent('initial', this, this.gMapContext.location);
29089     },
29090     
29091     initOverlayView: function()
29092     {
29093         var _this = this;
29094         
29095         Roo.bootstrap.LocationPicker.OverlayView.prototype = Roo.apply(new google.maps.OverlayView(), {
29096             
29097             draw: function()
29098             {
29099                 _this.fireEvent('OverlayViewDraw', _this);
29100             },
29101             
29102             onAdd: function()
29103             {
29104                 _this.fireEvent('OverlayViewOnAdd', _this);
29105             },
29106             
29107             onRemove: function()
29108             {
29109                 _this.fireEvent('OverlayViewOnRemove', _this);
29110             },
29111             
29112             show: function(cpx)
29113             {
29114                 _this.fireEvent('OverlayViewShow', _this, cpx);
29115             },
29116             
29117             hide: function()
29118             {
29119                 _this.fireEvent('OverlayViewHide', _this);
29120             }
29121             
29122         });
29123     },
29124     
29125     fromLatLngToContainerPixel: function(event)
29126     {
29127         return this.OverlayView.getProjection().fromLatLngToContainerPixel(event.latLng);
29128     },
29129     
29130     isApplied: function() 
29131     {
29132         return this.getGmapContext() == false ? false : true;
29133     },
29134     
29135     getGmapContext: function() 
29136     {
29137         return (typeof(this.gMapContext) == 'undefined') ? false : this.gMapContext;
29138     },
29139     
29140     GMapContext: function() 
29141     {
29142         var position = new google.maps.LatLng(this.latitude, this.longitude);
29143         
29144         var _map = new google.maps.Map(this.el.dom, {
29145             center: position,
29146             zoom: this.zoom,
29147             mapTypeId: this.mapTypeId,
29148             mapTypeControl: this.mapTypeControl,
29149             disableDoubleClickZoom: this.disableDoubleClickZoom,
29150             scrollwheel: this.scrollwheel,
29151             streetViewControl: this.streetViewControl,
29152             locationName: this.locationName,
29153             draggable: this.draggable,
29154             enableAutocomplete: this.enableAutocomplete,
29155             enableReverseGeocode: this.enableReverseGeocode
29156         });
29157         
29158         var _marker = new google.maps.Marker({
29159             position: position,
29160             map: _map,
29161             title: this.markerTitle,
29162             draggable: this.draggable
29163         });
29164         
29165         return {
29166             map: _map,
29167             marker: _marker,
29168             circle: null,
29169             location: position,
29170             radius: this.radius,
29171             locationName: this.locationName,
29172             addressComponents: {
29173                 formatted_address: null,
29174                 addressLine1: null,
29175                 addressLine2: null,
29176                 streetName: null,
29177                 streetNumber: null,
29178                 city: null,
29179                 district: null,
29180                 state: null,
29181                 stateOrProvince: null
29182             },
29183             settings: this,
29184             domContainer: this.el.dom,
29185             geodecoder: new google.maps.Geocoder()
29186         };
29187     },
29188     
29189     drawCircle: function(center, radius, options) 
29190     {
29191         if (this.gMapContext.circle != null) {
29192             this.gMapContext.circle.setMap(null);
29193         }
29194         if (radius > 0) {
29195             radius *= 1;
29196             options = Roo.apply({}, options, {
29197                 strokeColor: "#0000FF",
29198                 strokeOpacity: .35,
29199                 strokeWeight: 2,
29200                 fillColor: "#0000FF",
29201                 fillOpacity: .2
29202             });
29203             
29204             options.map = this.gMapContext.map;
29205             options.radius = radius;
29206             options.center = center;
29207             this.gMapContext.circle = new google.maps.Circle(options);
29208             return this.gMapContext.circle;
29209         }
29210         
29211         return null;
29212     },
29213     
29214     setPosition: function(location) 
29215     {
29216         this.gMapContext.location = location;
29217         this.gMapContext.marker.setPosition(location);
29218         this.gMapContext.map.panTo(location);
29219         this.drawCircle(location, this.gMapContext.radius, {});
29220         
29221         var _this = this;
29222         
29223         if (this.gMapContext.settings.enableReverseGeocode) {
29224             this.gMapContext.geodecoder.geocode({
29225                 latLng: this.gMapContext.location
29226             }, function(results, status) {
29227                 
29228                 if (status == google.maps.GeocoderStatus.OK && results.length > 0) {
29229                     _this.gMapContext.locationName = results[0].formatted_address;
29230                     _this.gMapContext.addressComponents = _this.address_component_from_google_geocode(results[0].address_components);
29231                     
29232                     _this.fireEvent('positionchanged', this, location);
29233                 }
29234             });
29235             
29236             return;
29237         }
29238         
29239         this.fireEvent('positionchanged', this, location);
29240     },
29241     
29242     resize: function()
29243     {
29244         google.maps.event.trigger(this.gMapContext.map, "resize");
29245         
29246         this.gMapContext.map.setCenter(this.gMapContext.marker.position);
29247         
29248         this.fireEvent('resize', this);
29249     },
29250     
29251     setPositionByLatLng: function(latitude, longitude)
29252     {
29253         this.setPosition(new google.maps.LatLng(latitude, longitude));
29254     },
29255     
29256     getCurrentPosition: function() 
29257     {
29258         return {
29259             latitude: this.gMapContext.location.lat(),
29260             longitude: this.gMapContext.location.lng()
29261         };
29262     },
29263     
29264     getAddressName: function() 
29265     {
29266         return this.gMapContext.locationName;
29267     },
29268     
29269     getAddressComponents: function() 
29270     {
29271         return this.gMapContext.addressComponents;
29272     },
29273     
29274     address_component_from_google_geocode: function(address_components) 
29275     {
29276         var result = {};
29277         
29278         for (var i = 0; i < address_components.length; i++) {
29279             var component = address_components[i];
29280             if (component.types.indexOf("postal_code") >= 0) {
29281                 result.postalCode = component.short_name;
29282             } else if (component.types.indexOf("street_number") >= 0) {
29283                 result.streetNumber = component.short_name;
29284             } else if (component.types.indexOf("route") >= 0) {
29285                 result.streetName = component.short_name;
29286             } else if (component.types.indexOf("neighborhood") >= 0) {
29287                 result.city = component.short_name;
29288             } else if (component.types.indexOf("locality") >= 0) {
29289                 result.city = component.short_name;
29290             } else if (component.types.indexOf("sublocality") >= 0) {
29291                 result.district = component.short_name;
29292             } else if (component.types.indexOf("administrative_area_level_1") >= 0) {
29293                 result.stateOrProvince = component.short_name;
29294             } else if (component.types.indexOf("country") >= 0) {
29295                 result.country = component.short_name;
29296             }
29297         }
29298         
29299         result.addressLine1 = [ result.streetNumber, result.streetName ].join(" ").trim();
29300         result.addressLine2 = "";
29301         return result;
29302     },
29303     
29304     setZoomLevel: function(zoom)
29305     {
29306         this.gMapContext.map.setZoom(zoom);
29307     },
29308     
29309     show: function()
29310     {
29311         if(!this.el){
29312             return;
29313         }
29314         
29315         this.el.show();
29316         
29317         this.resize();
29318         
29319         this.fireEvent('show', this);
29320     },
29321     
29322     hide: function()
29323     {
29324         if(!this.el){
29325             return;
29326         }
29327         
29328         this.el.hide();
29329         
29330         this.fireEvent('hide', this);
29331     }
29332     
29333 });
29334
29335 Roo.apply(Roo.bootstrap.LocationPicker, {
29336     
29337     OverlayView : function(map, options)
29338     {
29339         options = options || {};
29340         
29341         this.setMap(map);
29342     }
29343     
29344     
29345 });/**
29346  * @class Roo.bootstrap.Alert
29347  * @extends Roo.bootstrap.Component
29348  * Bootstrap Alert class - shows an alert area box
29349  * eg
29350  * <div class="alert alert-danger" role="alert"><span class="fa fa-exclamation-triangle"></span><span class="sr-only">Error:</span>
29351   Enter a valid email address
29352 </div>
29353  * @licence LGPL
29354  * @cfg {String} title The title of alert
29355  * @cfg {String} html The content of alert
29356  * @cfg {String} weight (  success | info | warning | danger )
29357  * @cfg {String} faicon font-awesomeicon
29358  * 
29359  * @constructor
29360  * Create a new alert
29361  * @param {Object} config The config object
29362  */
29363
29364
29365 Roo.bootstrap.Alert = function(config){
29366     Roo.bootstrap.Alert.superclass.constructor.call(this, config);
29367     
29368 };
29369
29370 Roo.extend(Roo.bootstrap.Alert, Roo.bootstrap.Component,  {
29371     
29372     title: '',
29373     html: '',
29374     weight: false,
29375     faicon: false,
29376     
29377     getAutoCreate : function()
29378     {
29379         
29380         var cfg = {
29381             tag : 'div',
29382             cls : 'alert',
29383             cn : [
29384                 {
29385                     tag : 'i',
29386                     cls : 'roo-alert-icon'
29387                     
29388                 },
29389                 {
29390                     tag : 'b',
29391                     cls : 'roo-alert-title',
29392                     html : this.title
29393                 },
29394                 {
29395                     tag : 'span',
29396                     cls : 'roo-alert-text',
29397                     html : this.html
29398                 }
29399             ]
29400         };
29401         
29402         if(this.faicon){
29403             cfg.cn[0].cls += ' fa ' + this.faicon;
29404         }
29405         
29406         if(this.weight){
29407             cfg.cls += ' alert-' + this.weight;
29408         }
29409         
29410         return cfg;
29411     },
29412     
29413     initEvents: function() 
29414     {
29415         this.el.setVisibilityMode(Roo.Element.DISPLAY);
29416     },
29417     
29418     setTitle : function(str)
29419     {
29420         this.el.select('.roo-alert-title',true).first().dom.innerHTML = str;
29421     },
29422     
29423     setText : function(str)
29424     {
29425         this.el.select('.roo-alert-text',true).first().dom.innerHTML = str;
29426     },
29427     
29428     setWeight : function(weight)
29429     {
29430         if(this.weight){
29431             this.el.select('.alert',true).first().removeClass('alert-' + this.weight);
29432         }
29433         
29434         this.weight = weight;
29435         
29436         this.el.select('.alert',true).first().addClass('alert-' + this.weight);
29437     },
29438     
29439     setIcon : function(icon)
29440     {
29441         if(this.faicon){
29442             this.el.select('.roo-alert-icon',true).first().removeClass(['fa', 'fa-' + this.faicon]);
29443         }
29444         
29445         this.faicon = icon;
29446         
29447         this.el.select('.roo-alert-icon',true).first().addClass(['fa', 'fa-' + this.faicon]);
29448     },
29449     
29450     hide: function() 
29451     {
29452         this.el.hide();   
29453     },
29454     
29455     show: function() 
29456     {  
29457         this.el.show();   
29458     }
29459     
29460 });
29461
29462  
29463 /*
29464 * Licence: LGPL
29465 */
29466
29467 /**
29468  * @class Roo.bootstrap.UploadCropbox
29469  * @extends Roo.bootstrap.Component
29470  * Bootstrap UploadCropbox class
29471  * @cfg {String} emptyText show when image has been loaded
29472  * @cfg {String} rotateNotify show when image too small to rotate
29473  * @cfg {Number} errorTimeout default 3000
29474  * @cfg {Number} minWidth default 300
29475  * @cfg {Number} minHeight default 300
29476  * @cfg {Array} buttons default ['rotateLeft', 'pictureBtn', 'rotateRight']
29477  * @cfg {Boolean} isDocument (true|false) default false
29478  * @cfg {String} url action url
29479  * @cfg {String} paramName default 'imageUpload'
29480  * @cfg {String} method default POST
29481  * @cfg {Boolean} loadMask (true|false) default true
29482  * @cfg {Boolean} loadingText default 'Loading...'
29483  * 
29484  * @constructor
29485  * Create a new UploadCropbox
29486  * @param {Object} config The config object
29487  */
29488
29489 Roo.bootstrap.UploadCropbox = function(config){
29490     Roo.bootstrap.UploadCropbox.superclass.constructor.call(this, config);
29491     
29492     this.addEvents({
29493         /**
29494          * @event beforeselectfile
29495          * Fire before select file
29496          * @param {Roo.bootstrap.UploadCropbox} this
29497          */
29498         "beforeselectfile" : true,
29499         /**
29500          * @event initial
29501          * Fire after initEvent
29502          * @param {Roo.bootstrap.UploadCropbox} this
29503          */
29504         "initial" : true,
29505         /**
29506          * @event crop
29507          * Fire after initEvent
29508          * @param {Roo.bootstrap.UploadCropbox} this
29509          * @param {String} data
29510          */
29511         "crop" : true,
29512         /**
29513          * @event prepare
29514          * Fire when preparing the file data
29515          * @param {Roo.bootstrap.UploadCropbox} this
29516          * @param {Object} file
29517          */
29518         "prepare" : true,
29519         /**
29520          * @event exception
29521          * Fire when get exception
29522          * @param {Roo.bootstrap.UploadCropbox} this
29523          * @param {XMLHttpRequest} xhr
29524          */
29525         "exception" : true,
29526         /**
29527          * @event beforeloadcanvas
29528          * Fire before load the canvas
29529          * @param {Roo.bootstrap.UploadCropbox} this
29530          * @param {String} src
29531          */
29532         "beforeloadcanvas" : true,
29533         /**
29534          * @event trash
29535          * Fire when trash image
29536          * @param {Roo.bootstrap.UploadCropbox} this
29537          */
29538         "trash" : true,
29539         /**
29540          * @event download
29541          * Fire when download the image
29542          * @param {Roo.bootstrap.UploadCropbox} this
29543          */
29544         "download" : true,
29545         /**
29546          * @event footerbuttonclick
29547          * Fire when footerbuttonclick
29548          * @param {Roo.bootstrap.UploadCropbox} this
29549          * @param {String} type
29550          */
29551         "footerbuttonclick" : true,
29552         /**
29553          * @event resize
29554          * Fire when resize
29555          * @param {Roo.bootstrap.UploadCropbox} this
29556          */
29557         "resize" : true,
29558         /**
29559          * @event rotate
29560          * Fire when rotate the image
29561          * @param {Roo.bootstrap.UploadCropbox} this
29562          * @param {String} pos
29563          */
29564         "rotate" : true,
29565         /**
29566          * @event inspect
29567          * Fire when inspect the file
29568          * @param {Roo.bootstrap.UploadCropbox} this
29569          * @param {Object} file
29570          */
29571         "inspect" : true,
29572         /**
29573          * @event upload
29574          * Fire when xhr upload the file
29575          * @param {Roo.bootstrap.UploadCropbox} this
29576          * @param {Object} data
29577          */
29578         "upload" : true,
29579         /**
29580          * @event arrange
29581          * Fire when arrange the file data
29582          * @param {Roo.bootstrap.UploadCropbox} this
29583          * @param {Object} formData
29584          */
29585         "arrange" : true
29586     });
29587     
29588     this.buttons = this.buttons || Roo.bootstrap.UploadCropbox.footer.STANDARD;
29589 };
29590
29591 Roo.extend(Roo.bootstrap.UploadCropbox, Roo.bootstrap.Component,  {
29592     
29593     emptyText : 'Click to upload image',
29594     rotateNotify : 'Image is too small to rotate',
29595     errorTimeout : 3000,
29596     scale : 0,
29597     baseScale : 1,
29598     rotate : 0,
29599     dragable : false,
29600     pinching : false,
29601     mouseX : 0,
29602     mouseY : 0,
29603     cropData : false,
29604     minWidth : 300,
29605     minHeight : 300,
29606     file : false,
29607     exif : {},
29608     baseRotate : 1,
29609     cropType : 'image/jpeg',
29610     buttons : false,
29611     canvasLoaded : false,
29612     isDocument : false,
29613     method : 'POST',
29614     paramName : 'imageUpload',
29615     loadMask : true,
29616     loadingText : 'Loading...',
29617     maskEl : false,
29618     
29619     getAutoCreate : function()
29620     {
29621         var cfg = {
29622             tag : 'div',
29623             cls : 'roo-upload-cropbox',
29624             cn : [
29625                 {
29626                     tag : 'input',
29627                     cls : 'roo-upload-cropbox-selector',
29628                     type : 'file'
29629                 },
29630                 {
29631                     tag : 'div',
29632                     cls : 'roo-upload-cropbox-body',
29633                     style : 'cursor:pointer',
29634                     cn : [
29635                         {
29636                             tag : 'div',
29637                             cls : 'roo-upload-cropbox-preview'
29638                         },
29639                         {
29640                             tag : 'div',
29641                             cls : 'roo-upload-cropbox-thumb'
29642                         },
29643                         {
29644                             tag : 'div',
29645                             cls : 'roo-upload-cropbox-empty-notify',
29646                             html : this.emptyText
29647                         },
29648                         {
29649                             tag : 'div',
29650                             cls : 'roo-upload-cropbox-error-notify alert alert-danger',
29651                             html : this.rotateNotify
29652                         }
29653                     ]
29654                 },
29655                 {
29656                     tag : 'div',
29657                     cls : 'roo-upload-cropbox-footer',
29658                     cn : {
29659                         tag : 'div',
29660                         cls : 'btn-group btn-group-justified roo-upload-cropbox-btn-group',
29661                         cn : []
29662                     }
29663                 }
29664             ]
29665         };
29666         
29667         return cfg;
29668     },
29669     
29670     onRender : function(ct, position)
29671     {
29672         Roo.bootstrap.UploadCropbox.superclass.onRender.call(this, ct, position);
29673         
29674         if (this.buttons.length) {
29675             
29676             Roo.each(this.buttons, function(bb) {
29677                 
29678                 var btn = this.el.select('.roo-upload-cropbox-footer div.roo-upload-cropbox-btn-group').first().createChild(bb);
29679                 
29680                 btn.on('click', this.onFooterButtonClick.createDelegate(this, [bb.action], true));
29681                 
29682             }, this);
29683         }
29684         
29685         if(this.loadMask){
29686             this.maskEl = this.el;
29687         }
29688     },
29689     
29690     initEvents : function()
29691     {
29692         this.urlAPI = (window.createObjectURL && window) || 
29693                                 (window.URL && URL.revokeObjectURL && URL) || 
29694                                 (window.webkitURL && webkitURL);
29695                         
29696         this.bodyEl = this.el.select('.roo-upload-cropbox-body', true).first();
29697         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29698         
29699         this.selectorEl = this.el.select('.roo-upload-cropbox-selector', true).first();
29700         this.selectorEl.hide();
29701         
29702         this.previewEl = this.el.select('.roo-upload-cropbox-preview', true).first();
29703         this.previewEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29704         
29705         this.thumbEl = this.el.select('.roo-upload-cropbox-thumb', true).first();
29706         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29707         this.thumbEl.hide();
29708         
29709         this.notifyEl = this.el.select('.roo-upload-cropbox-empty-notify', true).first();
29710         this.notifyEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29711         
29712         this.errorEl = this.el.select('.roo-upload-cropbox-error-notify', true).first();
29713         this.errorEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29714         this.errorEl.hide();
29715         
29716         this.footerEl = this.el.select('.roo-upload-cropbox-footer', true).first();
29717         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
29718         this.footerEl.hide();
29719         
29720         this.setThumbBoxSize();
29721         
29722         this.bind();
29723         
29724         this.resize();
29725         
29726         this.fireEvent('initial', this);
29727     },
29728
29729     bind : function()
29730     {
29731         var _this = this;
29732         
29733         window.addEventListener("resize", function() { _this.resize(); } );
29734         
29735         this.bodyEl.on('click', this.beforeSelectFile, this);
29736         
29737         if(Roo.isTouch){
29738             this.bodyEl.on('touchstart', this.onTouchStart, this);
29739             this.bodyEl.on('touchmove', this.onTouchMove, this);
29740             this.bodyEl.on('touchend', this.onTouchEnd, this);
29741         }
29742         
29743         if(!Roo.isTouch){
29744             this.bodyEl.on('mousedown', this.onMouseDown, this);
29745             this.bodyEl.on('mousemove', this.onMouseMove, this);
29746             var mousewheel = (/Firefox/i.test(navigator.userAgent))? 'DOMMouseScroll' : 'mousewheel';
29747             this.bodyEl.on(mousewheel, this.onMouseWheel, this);
29748             Roo.get(document).on('mouseup', this.onMouseUp, this);
29749         }
29750         
29751         this.selectorEl.on('change', this.onFileSelected, this);
29752     },
29753     
29754     reset : function()
29755     {    
29756         this.scale = 0;
29757         this.baseScale = 1;
29758         this.rotate = 0;
29759         this.baseRotate = 1;
29760         this.dragable = false;
29761         this.pinching = false;
29762         this.mouseX = 0;
29763         this.mouseY = 0;
29764         this.cropData = false;
29765         this.notifyEl.dom.innerHTML = this.emptyText;
29766         
29767         this.selectorEl.dom.value = '';
29768         
29769     },
29770     
29771     resize : function()
29772     {
29773         if(this.fireEvent('resize', this) != false){
29774             this.setThumbBoxPosition();
29775             this.setCanvasPosition();
29776         }
29777     },
29778     
29779     onFooterButtonClick : function(e, el, o, type)
29780     {
29781         switch (type) {
29782             case 'rotate-left' :
29783                 this.onRotateLeft(e);
29784                 break;
29785             case 'rotate-right' :
29786                 this.onRotateRight(e);
29787                 break;
29788             case 'picture' :
29789                 this.beforeSelectFile(e);
29790                 break;
29791             case 'trash' :
29792                 this.trash(e);
29793                 break;
29794             case 'crop' :
29795                 this.crop(e);
29796                 break;
29797             case 'download' :
29798                 this.download(e);
29799                 break;
29800             default :
29801                 break;
29802         }
29803         
29804         this.fireEvent('footerbuttonclick', this, type);
29805     },
29806     
29807     beforeSelectFile : function(e)
29808     {
29809         e.preventDefault();
29810         
29811         if(this.fireEvent('beforeselectfile', this) != false){
29812             this.selectorEl.dom.click();
29813         }
29814     },
29815     
29816     onFileSelected : function(e)
29817     {
29818         e.preventDefault();
29819         
29820         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
29821             return;
29822         }
29823         
29824         var file = this.selectorEl.dom.files[0];
29825         
29826         if(this.fireEvent('inspect', this, file) != false){
29827             this.prepare(file);
29828         }
29829         
29830     },
29831     
29832     trash : function(e)
29833     {
29834         this.fireEvent('trash', this);
29835     },
29836     
29837     download : function(e)
29838     {
29839         this.fireEvent('download', this);
29840     },
29841     
29842     loadCanvas : function(src)
29843     {   
29844         if(this.fireEvent('beforeloadcanvas', this, src) != false){
29845             
29846             this.reset();
29847             
29848             this.imageEl = document.createElement('img');
29849             
29850             var _this = this;
29851             
29852             this.imageEl.addEventListener("load", function(){ _this.onLoadCanvas(); });
29853             
29854             this.imageEl.src = src;
29855         }
29856     },
29857     
29858     onLoadCanvas : function()
29859     {   
29860         this.imageEl.OriginWidth = this.imageEl.naturalWidth || this.imageEl.width;
29861         this.imageEl.OriginHeight = this.imageEl.naturalHeight || this.imageEl.height;
29862         
29863         this.bodyEl.un('click', this.beforeSelectFile, this);
29864         
29865         this.notifyEl.hide();
29866         this.thumbEl.show();
29867         this.footerEl.show();
29868         
29869         this.baseRotateLevel();
29870         
29871         if(this.isDocument){
29872             this.setThumbBoxSize();
29873         }
29874         
29875         this.setThumbBoxPosition();
29876         
29877         this.baseScaleLevel();
29878         
29879         this.draw();
29880         
29881         this.resize();
29882         
29883         this.canvasLoaded = true;
29884         
29885         if(this.loadMask){
29886             this.maskEl.unmask();
29887         }
29888         
29889     },
29890     
29891     setCanvasPosition : function()
29892     {   
29893         if(!this.canvasEl){
29894             return;
29895         }
29896         
29897         var pw = Math.ceil((this.bodyEl.getWidth() - this.canvasEl.width) / 2);
29898         var ph = Math.ceil((this.bodyEl.getHeight() - this.canvasEl.height) / 2);
29899         
29900         this.previewEl.setLeft(pw);
29901         this.previewEl.setTop(ph);
29902         
29903     },
29904     
29905     onMouseDown : function(e)
29906     {   
29907         e.stopEvent();
29908         
29909         this.dragable = true;
29910         this.pinching = false;
29911         
29912         if(this.isDocument && (this.canvasEl.width < this.thumbEl.getWidth() || this.canvasEl.height < this.thumbEl.getHeight())){
29913             this.dragable = false;
29914             return;
29915         }
29916         
29917         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29918         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29919         
29920     },
29921     
29922     onMouseMove : function(e)
29923     {   
29924         e.stopEvent();
29925         
29926         if(!this.canvasLoaded){
29927             return;
29928         }
29929         
29930         if (!this.dragable){
29931             return;
29932         }
29933         
29934         var minX = Math.ceil(this.thumbEl.getLeft(true));
29935         var minY = Math.ceil(this.thumbEl.getTop(true));
29936         
29937         var maxX = Math.ceil(minX + this.thumbEl.getWidth() - this.canvasEl.width);
29938         var maxY = Math.ceil(minY + this.thumbEl.getHeight() - this.canvasEl.height);
29939         
29940         var x = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29941         var y = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29942         
29943         x = x - this.mouseX;
29944         y = y - this.mouseY;
29945         
29946         var bgX = Math.ceil(x + this.previewEl.getLeft(true));
29947         var bgY = Math.ceil(y + this.previewEl.getTop(true));
29948         
29949         bgX = (minX < bgX) ? minX : ((maxX > bgX) ? maxX : bgX);
29950         bgY = (minY < bgY) ? minY : ((maxY > bgY) ? maxY : bgY);
29951         
29952         this.previewEl.setLeft(bgX);
29953         this.previewEl.setTop(bgY);
29954         
29955         this.mouseX = Roo.isTouch ? e.browserEvent.touches[0].pageX : e.getPageX();
29956         this.mouseY = Roo.isTouch ? e.browserEvent.touches[0].pageY : e.getPageY();
29957     },
29958     
29959     onMouseUp : function(e)
29960     {   
29961         e.stopEvent();
29962         
29963         this.dragable = false;
29964     },
29965     
29966     onMouseWheel : function(e)
29967     {   
29968         e.stopEvent();
29969         
29970         this.startScale = this.scale;
29971         
29972         this.scale = (e.getWheelDelta() == 1) ? (this.scale + 1) : (this.scale - 1);
29973         
29974         if(!this.zoomable()){
29975             this.scale = this.startScale;
29976             return;
29977         }
29978         
29979         this.draw();
29980         
29981         return;
29982     },
29983     
29984     zoomable : function()
29985     {
29986         var minScale = this.thumbEl.getWidth() / this.minWidth;
29987         
29988         if(this.minWidth < this.minHeight){
29989             minScale = this.thumbEl.getHeight() / this.minHeight;
29990         }
29991         
29992         var width = Math.ceil(this.imageEl.OriginWidth * this.getScaleLevel() / minScale);
29993         var height = Math.ceil(this.imageEl.OriginHeight * this.getScaleLevel() / minScale);
29994         
29995         if(
29996                 this.isDocument &&
29997                 (this.rotate == 0 || this.rotate == 180) && 
29998                 (
29999                     width > this.imageEl.OriginWidth || 
30000                     height > this.imageEl.OriginHeight ||
30001                     (width < this.minWidth && height < this.minHeight)
30002                 )
30003         ){
30004             return false;
30005         }
30006         
30007         if(
30008                 this.isDocument &&
30009                 (this.rotate == 90 || this.rotate == 270) && 
30010                 (
30011                     width > this.imageEl.OriginWidth || 
30012                     height > this.imageEl.OriginHeight ||
30013                     (width < this.minHeight && height < this.minWidth)
30014                 )
30015         ){
30016             return false;
30017         }
30018         
30019         if(
30020                 !this.isDocument &&
30021                 (this.rotate == 0 || this.rotate == 180) && 
30022                 (
30023                     width < this.minWidth || 
30024                     width > this.imageEl.OriginWidth || 
30025                     height < this.minHeight || 
30026                     height > this.imageEl.OriginHeight
30027                 )
30028         ){
30029             return false;
30030         }
30031         
30032         if(
30033                 !this.isDocument &&
30034                 (this.rotate == 90 || this.rotate == 270) && 
30035                 (
30036                     width < this.minHeight || 
30037                     width > this.imageEl.OriginWidth || 
30038                     height < this.minWidth || 
30039                     height > this.imageEl.OriginHeight
30040                 )
30041         ){
30042             return false;
30043         }
30044         
30045         return true;
30046         
30047     },
30048     
30049     onRotateLeft : function(e)
30050     {   
30051         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30052             
30053             var minScale = this.thumbEl.getWidth() / this.minWidth;
30054             
30055             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30056             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30057             
30058             this.startScale = this.scale;
30059             
30060             while (this.getScaleLevel() < minScale){
30061             
30062                 this.scale = this.scale + 1;
30063                 
30064                 if(!this.zoomable()){
30065                     break;
30066                 }
30067                 
30068                 if(
30069                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30070                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30071                 ){
30072                     continue;
30073                 }
30074                 
30075                 this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30076
30077                 this.draw();
30078                 
30079                 return;
30080             }
30081             
30082             this.scale = this.startScale;
30083             
30084             this.onRotateFail();
30085             
30086             return false;
30087         }
30088         
30089         this.rotate = (this.rotate < 90) ? 270 : this.rotate - 90;
30090
30091         if(this.isDocument){
30092             this.setThumbBoxSize();
30093             this.setThumbBoxPosition();
30094             this.setCanvasPosition();
30095         }
30096         
30097         this.draw();
30098         
30099         this.fireEvent('rotate', this, 'left');
30100         
30101     },
30102     
30103     onRotateRight : function(e)
30104     {
30105         if(!this.isDocument && (this.canvasEl.height < this.thumbEl.getWidth() || this.canvasEl.width < this.thumbEl.getHeight())){
30106             
30107             var minScale = this.thumbEl.getWidth() / this.minWidth;
30108         
30109             var bw = Math.ceil(this.canvasEl.width / this.getScaleLevel());
30110             var bh = Math.ceil(this.canvasEl.height / this.getScaleLevel());
30111             
30112             this.startScale = this.scale;
30113             
30114             while (this.getScaleLevel() < minScale){
30115             
30116                 this.scale = this.scale + 1;
30117                 
30118                 if(!this.zoomable()){
30119                     break;
30120                 }
30121                 
30122                 if(
30123                         Math.ceil(bw * this.getScaleLevel()) < this.thumbEl.getHeight() ||
30124                         Math.ceil(bh * this.getScaleLevel()) < this.thumbEl.getWidth()
30125                 ){
30126                     continue;
30127                 }
30128                 
30129                 this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30130
30131                 this.draw();
30132                 
30133                 return;
30134             }
30135             
30136             this.scale = this.startScale;
30137             
30138             this.onRotateFail();
30139             
30140             return false;
30141         }
30142         
30143         this.rotate = (this.rotate > 180) ? 0 : this.rotate + 90;
30144
30145         if(this.isDocument){
30146             this.setThumbBoxSize();
30147             this.setThumbBoxPosition();
30148             this.setCanvasPosition();
30149         }
30150         
30151         this.draw();
30152         
30153         this.fireEvent('rotate', this, 'right');
30154     },
30155     
30156     onRotateFail : function()
30157     {
30158         this.errorEl.show(true);
30159         
30160         var _this = this;
30161         
30162         (function() { _this.errorEl.hide(true); }).defer(this.errorTimeout);
30163     },
30164     
30165     draw : function()
30166     {
30167         this.previewEl.dom.innerHTML = '';
30168         
30169         var canvasEl = document.createElement("canvas");
30170         
30171         var contextEl = canvasEl.getContext("2d");
30172         
30173         canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30174         canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30175         var center = this.imageEl.OriginWidth / 2;
30176         
30177         if(this.imageEl.OriginWidth < this.imageEl.OriginHeight){
30178             canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30179             canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30180             center = this.imageEl.OriginHeight / 2;
30181         }
30182         
30183         contextEl.scale(this.getScaleLevel(), this.getScaleLevel());
30184         
30185         contextEl.translate(center, center);
30186         contextEl.rotate(this.rotate * Math.PI / 180);
30187
30188         contextEl.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30189         
30190         this.canvasEl = document.createElement("canvas");
30191         
30192         this.contextEl = this.canvasEl.getContext("2d");
30193         
30194         switch (this.rotate) {
30195             case 0 :
30196                 
30197                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30198                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30199                 
30200                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30201                 
30202                 break;
30203             case 90 : 
30204                 
30205                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30206                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30207                 
30208                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30209                     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);
30210                     break;
30211                 }
30212                 
30213                 this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30214                 
30215                 break;
30216             case 180 :
30217                 
30218                 this.canvasEl.width = this.imageEl.OriginWidth * this.getScaleLevel();
30219                 this.canvasEl.height = this.imageEl.OriginHeight * this.getScaleLevel();
30220                 
30221                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30222                     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);
30223                     break;
30224                 }
30225                 
30226                 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);
30227                 
30228                 break;
30229             case 270 :
30230                 
30231                 this.canvasEl.width = this.imageEl.OriginHeight * this.getScaleLevel();
30232                 this.canvasEl.height = this.imageEl.OriginWidth * this.getScaleLevel();
30233         
30234                 if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30235                     this.contextEl.drawImage(canvasEl, 0, 0, this.canvasEl.width, this.canvasEl.height, 0, 0, this.canvasEl.width, this.canvasEl.height);
30236                     break;
30237                 }
30238                 
30239                 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);
30240                 
30241                 break;
30242             default : 
30243                 break;
30244         }
30245         
30246         this.previewEl.appendChild(this.canvasEl);
30247         
30248         this.setCanvasPosition();
30249     },
30250     
30251     crop : function()
30252     {
30253         if(!this.canvasLoaded){
30254             return;
30255         }
30256         
30257         var imageCanvas = document.createElement("canvas");
30258         
30259         var imageContext = imageCanvas.getContext("2d");
30260         
30261         imageCanvas.width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30262         imageCanvas.height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? this.imageEl.OriginWidth : this.imageEl.OriginHeight;
30263         
30264         var center = imageCanvas.width / 2;
30265         
30266         imageContext.translate(center, center);
30267         
30268         imageContext.rotate(this.rotate * Math.PI / 180);
30269         
30270         imageContext.drawImage(this.imageEl, 0, 0, this.imageEl.OriginWidth, this.imageEl.OriginHeight, center * -1, center * -1, this.imageEl.OriginWidth, this.imageEl.OriginHeight);
30271         
30272         var canvas = document.createElement("canvas");
30273         
30274         var context = canvas.getContext("2d");
30275                 
30276         canvas.width = this.minWidth;
30277         canvas.height = this.minHeight;
30278
30279         switch (this.rotate) {
30280             case 0 :
30281                 
30282                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30283                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30284                 
30285                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30286                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30287                 
30288                 var targetWidth = this.minWidth - 2 * x;
30289                 var targetHeight = this.minHeight - 2 * y;
30290                 
30291                 var scale = 1;
30292                 
30293                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30294                     scale = targetWidth / width;
30295                 }
30296                 
30297                 if(x > 0 && y == 0){
30298                     scale = targetHeight / height;
30299                 }
30300                 
30301                 if(x > 0 && y > 0){
30302                     scale = targetWidth / width;
30303                     
30304                     if(width < height){
30305                         scale = targetHeight / height;
30306                     }
30307                 }
30308                 
30309                 context.scale(scale, scale);
30310                 
30311                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30312                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30313
30314                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30315                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30316
30317                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30318                 
30319                 break;
30320             case 90 : 
30321                 
30322                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30323                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30324                 
30325                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30326                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30327                 
30328                 var targetWidth = this.minWidth - 2 * x;
30329                 var targetHeight = this.minHeight - 2 * y;
30330                 
30331                 var scale = 1;
30332                 
30333                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30334                     scale = targetWidth / width;
30335                 }
30336                 
30337                 if(x > 0 && y == 0){
30338                     scale = targetHeight / height;
30339                 }
30340                 
30341                 if(x > 0 && y > 0){
30342                     scale = targetWidth / width;
30343                     
30344                     if(width < height){
30345                         scale = targetHeight / height;
30346                     }
30347                 }
30348                 
30349                 context.scale(scale, scale);
30350                 
30351                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30352                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30353
30354                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30355                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30356                 
30357                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30358                 
30359                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30360                 
30361                 break;
30362             case 180 :
30363                 
30364                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getWidth() / this.getScaleLevel());
30365                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getHeight() / this.getScaleLevel());
30366                 
30367                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30368                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30369                 
30370                 var targetWidth = this.minWidth - 2 * x;
30371                 var targetHeight = this.minHeight - 2 * y;
30372                 
30373                 var scale = 1;
30374                 
30375                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30376                     scale = targetWidth / width;
30377                 }
30378                 
30379                 if(x > 0 && y == 0){
30380                     scale = targetHeight / height;
30381                 }
30382                 
30383                 if(x > 0 && y > 0){
30384                     scale = targetWidth / width;
30385                     
30386                     if(width < height){
30387                         scale = targetHeight / height;
30388                     }
30389                 }
30390                 
30391                 context.scale(scale, scale);
30392                 
30393                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30394                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30395
30396                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30397                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30398
30399                 sx += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30400                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight) : 0;
30401                 
30402                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30403                 
30404                 break;
30405             case 270 :
30406                 
30407                 var width = (this.thumbEl.getWidth() / this.getScaleLevel() > this.imageEl.OriginHeight) ? this.imageEl.OriginHeight : (this.thumbEl.getWidth() / this.getScaleLevel());
30408                 var height = (this.thumbEl.getHeight() / this.getScaleLevel() > this.imageEl.OriginWidth) ? this.imageEl.OriginWidth : (this.thumbEl.getHeight() / this.getScaleLevel());
30409                 
30410                 var x = (this.thumbEl.getLeft(true) > this.previewEl.getLeft(true)) ? 0 : ((this.previewEl.getLeft(true) - this.thumbEl.getLeft(true)) / this.getScaleLevel());
30411                 var y = (this.thumbEl.getTop(true) > this.previewEl.getTop(true)) ? 0 : ((this.previewEl.getTop(true) - this.thumbEl.getTop(true)) / this.getScaleLevel());
30412                 
30413                 var targetWidth = this.minWidth - 2 * x;
30414                 var targetHeight = this.minHeight - 2 * y;
30415                 
30416                 var scale = 1;
30417                 
30418                 if((x == 0 && y == 0) || (x == 0 && y > 0)){
30419                     scale = targetWidth / width;
30420                 }
30421                 
30422                 if(x > 0 && y == 0){
30423                     scale = targetHeight / height;
30424                 }
30425                 
30426                 if(x > 0 && y > 0){
30427                     scale = targetWidth / width;
30428                     
30429                     if(width < height){
30430                         scale = targetHeight / height;
30431                     }
30432                 }
30433                 
30434                 context.scale(scale, scale);
30435                 
30436                 var sx = Math.min(this.canvasEl.width - this.thumbEl.getWidth(), this.thumbEl.getLeft(true) - this.previewEl.getLeft(true));
30437                 var sy = Math.min(this.canvasEl.height - this.thumbEl.getHeight(), this.thumbEl.getTop(true) - this.previewEl.getTop(true));
30438
30439                 sx = sx < 0 ? 0 : (sx / this.getScaleLevel());
30440                 sy = sy < 0 ? 0 : (sy / this.getScaleLevel());
30441                 
30442                 sy += (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? 0 : Math.abs(this.imageEl.OriginWidth - this.imageEl.OriginHeight);
30443                 
30444                 context.drawImage(imageCanvas, sx, sy, width, height, x, y, width, height);
30445                 
30446                 break;
30447             default : 
30448                 break;
30449         }
30450         
30451         this.cropData = canvas.toDataURL(this.cropType);
30452         
30453         if(this.fireEvent('crop', this, this.cropData) !== false){
30454             this.process(this.file, this.cropData);
30455         }
30456         
30457         return;
30458         
30459     },
30460     
30461     setThumbBoxSize : function()
30462     {
30463         var width, height;
30464         
30465         if(this.isDocument && typeof(this.imageEl) != 'undefined'){
30466             width = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.max(this.minWidth, this.minHeight) : Math.min(this.minWidth, this.minHeight);
30467             height = (this.imageEl.OriginWidth > this.imageEl.OriginHeight) ? Math.min(this.minWidth, this.minHeight) : Math.max(this.minWidth, this.minHeight);
30468             
30469             this.minWidth = width;
30470             this.minHeight = height;
30471             
30472             if(this.rotate == 90 || this.rotate == 270){
30473                 this.minWidth = height;
30474                 this.minHeight = width;
30475             }
30476         }
30477         
30478         height = 300;
30479         width = Math.ceil(this.minWidth * height / this.minHeight);
30480         
30481         if(this.minWidth > this.minHeight){
30482             width = 300;
30483             height = Math.ceil(this.minHeight * width / this.minWidth);
30484         }
30485         
30486         this.thumbEl.setStyle({
30487             width : width + 'px',
30488             height : height + 'px'
30489         });
30490
30491         return;
30492             
30493     },
30494     
30495     setThumbBoxPosition : function()
30496     {
30497         var x = Math.ceil((this.bodyEl.getWidth() - this.thumbEl.getWidth()) / 2 );
30498         var y = Math.ceil((this.bodyEl.getHeight() - this.thumbEl.getHeight()) / 2);
30499         
30500         this.thumbEl.setLeft(x);
30501         this.thumbEl.setTop(y);
30502         
30503     },
30504     
30505     baseRotateLevel : function()
30506     {
30507         this.baseRotate = 1;
30508         
30509         if(
30510                 typeof(this.exif) != 'undefined' &&
30511                 typeof(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != 'undefined' &&
30512                 [1, 3, 6, 8].indexOf(this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']]) != -1
30513         ){
30514             this.baseRotate = this.exif[Roo.bootstrap.UploadCropbox['tags']['Orientation']];
30515         }
30516         
30517         this.rotate = Roo.bootstrap.UploadCropbox['Orientation'][this.baseRotate];
30518         
30519     },
30520     
30521     baseScaleLevel : function()
30522     {
30523         var width, height;
30524         
30525         if(this.isDocument){
30526             
30527             if(this.baseRotate == 6 || this.baseRotate == 8){
30528             
30529                 height = this.thumbEl.getHeight();
30530                 this.baseScale = height / this.imageEl.OriginWidth;
30531
30532                 if(this.imageEl.OriginHeight * this.baseScale > this.thumbEl.getWidth()){
30533                     width = this.thumbEl.getWidth();
30534                     this.baseScale = width / this.imageEl.OriginHeight;
30535                 }
30536
30537                 return;
30538             }
30539
30540             height = this.thumbEl.getHeight();
30541             this.baseScale = height / this.imageEl.OriginHeight;
30542
30543             if(this.imageEl.OriginWidth * this.baseScale > this.thumbEl.getWidth()){
30544                 width = this.thumbEl.getWidth();
30545                 this.baseScale = width / this.imageEl.OriginWidth;
30546             }
30547
30548             return;
30549         }
30550         
30551         if(this.baseRotate == 6 || this.baseRotate == 8){
30552             
30553             width = this.thumbEl.getHeight();
30554             this.baseScale = width / this.imageEl.OriginHeight;
30555             
30556             if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getWidth()){
30557                 height = this.thumbEl.getWidth();
30558                 this.baseScale = height / this.imageEl.OriginHeight;
30559             }
30560             
30561             if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30562                 height = this.thumbEl.getWidth();
30563                 this.baseScale = height / this.imageEl.OriginHeight;
30564                 
30565                 if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getHeight()){
30566                     width = this.thumbEl.getHeight();
30567                     this.baseScale = width / this.imageEl.OriginWidth;
30568                 }
30569             }
30570             
30571             return;
30572         }
30573         
30574         width = this.thumbEl.getWidth();
30575         this.baseScale = width / this.imageEl.OriginWidth;
30576         
30577         if(this.imageEl.OriginHeight * this.baseScale < this.thumbEl.getHeight()){
30578             height = this.thumbEl.getHeight();
30579             this.baseScale = height / this.imageEl.OriginHeight;
30580         }
30581         
30582         if(this.imageEl.OriginWidth > this.imageEl.OriginHeight){
30583             
30584             height = this.thumbEl.getHeight();
30585             this.baseScale = height / this.imageEl.OriginHeight;
30586             
30587             if(this.imageEl.OriginWidth * this.baseScale < this.thumbEl.getWidth()){
30588                 width = this.thumbEl.getWidth();
30589                 this.baseScale = width / this.imageEl.OriginWidth;
30590             }
30591             
30592         }
30593         
30594         return;
30595     },
30596     
30597     getScaleLevel : function()
30598     {
30599         return this.baseScale * Math.pow(1.1, this.scale);
30600     },
30601     
30602     onTouchStart : function(e)
30603     {
30604         if(!this.canvasLoaded){
30605             this.beforeSelectFile(e);
30606             return;
30607         }
30608         
30609         var touches = e.browserEvent.touches;
30610         
30611         if(!touches){
30612             return;
30613         }
30614         
30615         if(touches.length == 1){
30616             this.onMouseDown(e);
30617             return;
30618         }
30619         
30620         if(touches.length != 2){
30621             return;
30622         }
30623         
30624         var coords = [];
30625         
30626         for(var i = 0, finger; finger = touches[i]; i++){
30627             coords.push(finger.pageX, finger.pageY);
30628         }
30629         
30630         var x = Math.pow(coords[0] - coords[2], 2);
30631         var y = Math.pow(coords[1] - coords[3], 2);
30632         
30633         this.startDistance = Math.sqrt(x + y);
30634         
30635         this.startScale = this.scale;
30636         
30637         this.pinching = true;
30638         this.dragable = false;
30639         
30640     },
30641     
30642     onTouchMove : function(e)
30643     {
30644         if(!this.pinching && !this.dragable){
30645             return;
30646         }
30647         
30648         var touches = e.browserEvent.touches;
30649         
30650         if(!touches){
30651             return;
30652         }
30653         
30654         if(this.dragable){
30655             this.onMouseMove(e);
30656             return;
30657         }
30658         
30659         var coords = [];
30660         
30661         for(var i = 0, finger; finger = touches[i]; i++){
30662             coords.push(finger.pageX, finger.pageY);
30663         }
30664         
30665         var x = Math.pow(coords[0] - coords[2], 2);
30666         var y = Math.pow(coords[1] - coords[3], 2);
30667         
30668         this.endDistance = Math.sqrt(x + y);
30669         
30670         this.scale = this.startScale + Math.floor(Math.log(this.endDistance / this.startDistance) / Math.log(1.1));
30671         
30672         if(!this.zoomable()){
30673             this.scale = this.startScale;
30674             return;
30675         }
30676         
30677         this.draw();
30678         
30679     },
30680     
30681     onTouchEnd : function(e)
30682     {
30683         this.pinching = false;
30684         this.dragable = false;
30685         
30686     },
30687     
30688     process : function(file, crop)
30689     {
30690         if(this.loadMask){
30691             this.maskEl.mask(this.loadingText);
30692         }
30693         
30694         this.xhr = new XMLHttpRequest();
30695         
30696         file.xhr = this.xhr;
30697
30698         this.xhr.open(this.method, this.url, true);
30699         
30700         var headers = {
30701             "Accept": "application/json",
30702             "Cache-Control": "no-cache",
30703             "X-Requested-With": "XMLHttpRequest"
30704         };
30705         
30706         for (var headerName in headers) {
30707             var headerValue = headers[headerName];
30708             if (headerValue) {
30709                 this.xhr.setRequestHeader(headerName, headerValue);
30710             }
30711         }
30712         
30713         var _this = this;
30714         
30715         this.xhr.onload = function()
30716         {
30717             _this.xhrOnLoad(_this.xhr);
30718         }
30719         
30720         this.xhr.onerror = function()
30721         {
30722             _this.xhrOnError(_this.xhr);
30723         }
30724         
30725         var formData = new FormData();
30726
30727         formData.append('returnHTML', 'NO');
30728         
30729         if(crop){
30730             formData.append('crop', crop);
30731         }
30732         
30733         if(typeof(file) != 'undefined' && (typeof(file.id) == 'undefined' || file.id * 1 < 1)){
30734             formData.append(this.paramName, file, file.name);
30735         }
30736         
30737         if(typeof(file.filename) != 'undefined'){
30738             formData.append('filename', file.filename);
30739         }
30740         
30741         if(typeof(file.mimetype) != 'undefined'){
30742             formData.append('mimetype', file.mimetype);
30743         }
30744         
30745         if(this.fireEvent('arrange', this, formData) != false){
30746             this.xhr.send(formData);
30747         };
30748     },
30749     
30750     xhrOnLoad : function(xhr)
30751     {
30752         if(this.loadMask){
30753             this.maskEl.unmask();
30754         }
30755         
30756         if (xhr.readyState !== 4) {
30757             this.fireEvent('exception', this, xhr);
30758             return;
30759         }
30760
30761         var response = Roo.decode(xhr.responseText);
30762         
30763         if(!response.success){
30764             this.fireEvent('exception', this, xhr);
30765             return;
30766         }
30767         
30768         var response = Roo.decode(xhr.responseText);
30769         
30770         this.fireEvent('upload', this, response);
30771         
30772     },
30773     
30774     xhrOnError : function()
30775     {
30776         if(this.loadMask){
30777             this.maskEl.unmask();
30778         }
30779         
30780         Roo.log('xhr on error');
30781         
30782         var response = Roo.decode(xhr.responseText);
30783           
30784         Roo.log(response);
30785         
30786     },
30787     
30788     prepare : function(file)
30789     {   
30790         if(this.loadMask){
30791             this.maskEl.mask(this.loadingText);
30792         }
30793         
30794         this.file = false;
30795         this.exif = {};
30796         
30797         if(typeof(file) === 'string'){
30798             this.loadCanvas(file);
30799             return;
30800         }
30801         
30802         if(!file || !this.urlAPI){
30803             return;
30804         }
30805         
30806         this.file = file;
30807         this.cropType = file.type;
30808         
30809         var _this = this;
30810         
30811         if(this.fireEvent('prepare', this, this.file) != false){
30812             
30813             var reader = new FileReader();
30814             
30815             reader.onload = function (e) {
30816                 if (e.target.error) {
30817                     Roo.log(e.target.error);
30818                     return;
30819                 }
30820                 
30821                 var buffer = e.target.result,
30822                     dataView = new DataView(buffer),
30823                     offset = 2,
30824                     maxOffset = dataView.byteLength - 4,
30825                     markerBytes,
30826                     markerLength;
30827                 
30828                 if (dataView.getUint16(0) === 0xffd8) {
30829                     while (offset < maxOffset) {
30830                         markerBytes = dataView.getUint16(offset);
30831                         
30832                         if ((markerBytes >= 0xffe0 && markerBytes <= 0xffef) || markerBytes === 0xfffe) {
30833                             markerLength = dataView.getUint16(offset + 2) + 2;
30834                             if (offset + markerLength > dataView.byteLength) {
30835                                 Roo.log('Invalid meta data: Invalid segment size.');
30836                                 break;
30837                             }
30838                             
30839                             if(markerBytes == 0xffe1){
30840                                 _this.parseExifData(
30841                                     dataView,
30842                                     offset,
30843                                     markerLength
30844                                 );
30845                             }
30846                             
30847                             offset += markerLength;
30848                             
30849                             continue;
30850                         }
30851                         
30852                         break;
30853                     }
30854                     
30855                 }
30856                 
30857                 var url = _this.urlAPI.createObjectURL(_this.file);
30858                 
30859                 _this.loadCanvas(url);
30860                 
30861                 return;
30862             }
30863             
30864             reader.readAsArrayBuffer(this.file);
30865             
30866         }
30867         
30868     },
30869     
30870     parseExifData : function(dataView, offset, length)
30871     {
30872         var tiffOffset = offset + 10,
30873             littleEndian,
30874             dirOffset;
30875     
30876         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30877             // No Exif data, might be XMP data instead
30878             return;
30879         }
30880         
30881         // Check for the ASCII code for "Exif" (0x45786966):
30882         if (dataView.getUint32(offset + 4) !== 0x45786966) {
30883             // No Exif data, might be XMP data instead
30884             return;
30885         }
30886         if (tiffOffset + 8 > dataView.byteLength) {
30887             Roo.log('Invalid Exif data: Invalid segment size.');
30888             return;
30889         }
30890         // Check for the two null bytes:
30891         if (dataView.getUint16(offset + 8) !== 0x0000) {
30892             Roo.log('Invalid Exif data: Missing byte alignment offset.');
30893             return;
30894         }
30895         // Check the byte alignment:
30896         switch (dataView.getUint16(tiffOffset)) {
30897         case 0x4949:
30898             littleEndian = true;
30899             break;
30900         case 0x4D4D:
30901             littleEndian = false;
30902             break;
30903         default:
30904             Roo.log('Invalid Exif data: Invalid byte alignment marker.');
30905             return;
30906         }
30907         // Check for the TIFF tag marker (0x002A):
30908         if (dataView.getUint16(tiffOffset + 2, littleEndian) !== 0x002A) {
30909             Roo.log('Invalid Exif data: Missing TIFF marker.');
30910             return;
30911         }
30912         // Retrieve the directory offset bytes, usually 0x00000008 or 8 decimal:
30913         dirOffset = dataView.getUint32(tiffOffset + 4, littleEndian);
30914         
30915         this.parseExifTags(
30916             dataView,
30917             tiffOffset,
30918             tiffOffset + dirOffset,
30919             littleEndian
30920         );
30921     },
30922     
30923     parseExifTags : function(dataView, tiffOffset, dirOffset, littleEndian)
30924     {
30925         var tagsNumber,
30926             dirEndOffset,
30927             i;
30928         if (dirOffset + 6 > dataView.byteLength) {
30929             Roo.log('Invalid Exif data: Invalid directory offset.');
30930             return;
30931         }
30932         tagsNumber = dataView.getUint16(dirOffset, littleEndian);
30933         dirEndOffset = dirOffset + 2 + 12 * tagsNumber;
30934         if (dirEndOffset + 4 > dataView.byteLength) {
30935             Roo.log('Invalid Exif data: Invalid directory size.');
30936             return;
30937         }
30938         for (i = 0; i < tagsNumber; i += 1) {
30939             this.parseExifTag(
30940                 dataView,
30941                 tiffOffset,
30942                 dirOffset + 2 + 12 * i, // tag offset
30943                 littleEndian
30944             );
30945         }
30946         // Return the offset to the next directory:
30947         return dataView.getUint32(dirEndOffset, littleEndian);
30948     },
30949     
30950     parseExifTag : function (dataView, tiffOffset, offset, littleEndian) 
30951     {
30952         var tag = dataView.getUint16(offset, littleEndian);
30953         
30954         this.exif[tag] = this.getExifValue(
30955             dataView,
30956             tiffOffset,
30957             offset,
30958             dataView.getUint16(offset + 2, littleEndian), // tag type
30959             dataView.getUint32(offset + 4, littleEndian), // tag length
30960             littleEndian
30961         );
30962     },
30963     
30964     getExifValue : function (dataView, tiffOffset, offset, type, length, littleEndian)
30965     {
30966         var tagType = Roo.bootstrap.UploadCropbox.exifTagTypes[type],
30967             tagSize,
30968             dataOffset,
30969             values,
30970             i,
30971             str,
30972             c;
30973     
30974         if (!tagType) {
30975             Roo.log('Invalid Exif data: Invalid tag type.');
30976             return;
30977         }
30978         
30979         tagSize = tagType.size * length;
30980         // Determine if the value is contained in the dataOffset bytes,
30981         // or if the value at the dataOffset is a pointer to the actual data:
30982         dataOffset = tagSize > 4 ?
30983                 tiffOffset + dataView.getUint32(offset + 8, littleEndian) : (offset + 8);
30984         if (dataOffset + tagSize > dataView.byteLength) {
30985             Roo.log('Invalid Exif data: Invalid data offset.');
30986             return;
30987         }
30988         if (length === 1) {
30989             return tagType.getValue(dataView, dataOffset, littleEndian);
30990         }
30991         values = [];
30992         for (i = 0; i < length; i += 1) {
30993             values[i] = tagType.getValue(dataView, dataOffset + i * tagType.size, littleEndian);
30994         }
30995         
30996         if (tagType.ascii) {
30997             str = '';
30998             // Concatenate the chars:
30999             for (i = 0; i < values.length; i += 1) {
31000                 c = values[i];
31001                 // Ignore the terminating NULL byte(s):
31002                 if (c === '\u0000') {
31003                     break;
31004                 }
31005                 str += c;
31006             }
31007             return str;
31008         }
31009         return values;
31010     }
31011     
31012 });
31013
31014 Roo.apply(Roo.bootstrap.UploadCropbox, {
31015     tags : {
31016         'Orientation': 0x0112
31017     },
31018     
31019     Orientation: {
31020             1: 0, //'top-left',
31021 //            2: 'top-right',
31022             3: 180, //'bottom-right',
31023 //            4: 'bottom-left',
31024 //            5: 'left-top',
31025             6: 90, //'right-top',
31026 //            7: 'right-bottom',
31027             8: 270 //'left-bottom'
31028     },
31029     
31030     exifTagTypes : {
31031         // byte, 8-bit unsigned int:
31032         1: {
31033             getValue: function (dataView, dataOffset) {
31034                 return dataView.getUint8(dataOffset);
31035             },
31036             size: 1
31037         },
31038         // ascii, 8-bit byte:
31039         2: {
31040             getValue: function (dataView, dataOffset) {
31041                 return String.fromCharCode(dataView.getUint8(dataOffset));
31042             },
31043             size: 1,
31044             ascii: true
31045         },
31046         // short, 16 bit int:
31047         3: {
31048             getValue: function (dataView, dataOffset, littleEndian) {
31049                 return dataView.getUint16(dataOffset, littleEndian);
31050             },
31051             size: 2
31052         },
31053         // long, 32 bit int:
31054         4: {
31055             getValue: function (dataView, dataOffset, littleEndian) {
31056                 return dataView.getUint32(dataOffset, littleEndian);
31057             },
31058             size: 4
31059         },
31060         // rational = two long values, first is numerator, second is denominator:
31061         5: {
31062             getValue: function (dataView, dataOffset, littleEndian) {
31063                 return dataView.getUint32(dataOffset, littleEndian) /
31064                     dataView.getUint32(dataOffset + 4, littleEndian);
31065             },
31066             size: 8
31067         },
31068         // slong, 32 bit signed int:
31069         9: {
31070             getValue: function (dataView, dataOffset, littleEndian) {
31071                 return dataView.getInt32(dataOffset, littleEndian);
31072             },
31073             size: 4
31074         },
31075         // srational, two slongs, first is numerator, second is denominator:
31076         10: {
31077             getValue: function (dataView, dataOffset, littleEndian) {
31078                 return dataView.getInt32(dataOffset, littleEndian) /
31079                     dataView.getInt32(dataOffset + 4, littleEndian);
31080             },
31081             size: 8
31082         }
31083     },
31084     
31085     footer : {
31086         STANDARD : [
31087             {
31088                 tag : 'div',
31089                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31090                 action : 'rotate-left',
31091                 cn : [
31092                     {
31093                         tag : 'button',
31094                         cls : 'btn btn-default',
31095                         html : '<i class="fa fa-undo"></i>'
31096                     }
31097                 ]
31098             },
31099             {
31100                 tag : 'div',
31101                 cls : 'btn-group roo-upload-cropbox-picture',
31102                 action : 'picture',
31103                 cn : [
31104                     {
31105                         tag : 'button',
31106                         cls : 'btn btn-default',
31107                         html : '<i class="fa fa-picture-o"></i>'
31108                     }
31109                 ]
31110             },
31111             {
31112                 tag : 'div',
31113                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31114                 action : 'rotate-right',
31115                 cn : [
31116                     {
31117                         tag : 'button',
31118                         cls : 'btn btn-default',
31119                         html : '<i class="fa fa-repeat"></i>'
31120                     }
31121                 ]
31122             }
31123         ],
31124         DOCUMENT : [
31125             {
31126                 tag : 'div',
31127                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31128                 action : 'rotate-left',
31129                 cn : [
31130                     {
31131                         tag : 'button',
31132                         cls : 'btn btn-default',
31133                         html : '<i class="fa fa-undo"></i>'
31134                     }
31135                 ]
31136             },
31137             {
31138                 tag : 'div',
31139                 cls : 'btn-group roo-upload-cropbox-download',
31140                 action : 'download',
31141                 cn : [
31142                     {
31143                         tag : 'button',
31144                         cls : 'btn btn-default',
31145                         html : '<i class="fa fa-download"></i>'
31146                     }
31147                 ]
31148             },
31149             {
31150                 tag : 'div',
31151                 cls : 'btn-group roo-upload-cropbox-crop',
31152                 action : 'crop',
31153                 cn : [
31154                     {
31155                         tag : 'button',
31156                         cls : 'btn btn-default',
31157                         html : '<i class="fa fa-crop"></i>'
31158                     }
31159                 ]
31160             },
31161             {
31162                 tag : 'div',
31163                 cls : 'btn-group roo-upload-cropbox-trash',
31164                 action : 'trash',
31165                 cn : [
31166                     {
31167                         tag : 'button',
31168                         cls : 'btn btn-default',
31169                         html : '<i class="fa fa-trash"></i>'
31170                     }
31171                 ]
31172             },
31173             {
31174                 tag : 'div',
31175                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31176                 action : 'rotate-right',
31177                 cn : [
31178                     {
31179                         tag : 'button',
31180                         cls : 'btn btn-default',
31181                         html : '<i class="fa fa-repeat"></i>'
31182                     }
31183                 ]
31184             }
31185         ],
31186         ROTATOR : [
31187             {
31188                 tag : 'div',
31189                 cls : 'btn-group roo-upload-cropbox-rotate-left',
31190                 action : 'rotate-left',
31191                 cn : [
31192                     {
31193                         tag : 'button',
31194                         cls : 'btn btn-default',
31195                         html : '<i class="fa fa-undo"></i>'
31196                     }
31197                 ]
31198             },
31199             {
31200                 tag : 'div',
31201                 cls : 'btn-group roo-upload-cropbox-rotate-right',
31202                 action : 'rotate-right',
31203                 cn : [
31204                     {
31205                         tag : 'button',
31206                         cls : 'btn btn-default',
31207                         html : '<i class="fa fa-repeat"></i>'
31208                     }
31209                 ]
31210             }
31211         ]
31212     }
31213 });
31214
31215 /*
31216 * Licence: LGPL
31217 */
31218
31219 /**
31220  * @class Roo.bootstrap.DocumentManager
31221  * @extends Roo.bootstrap.Component
31222  * Bootstrap DocumentManager class
31223  * @cfg {String} paramName default 'imageUpload'
31224  * @cfg {String} toolTipName default 'filename'
31225  * @cfg {String} method default POST
31226  * @cfg {String} url action url
31227  * @cfg {Number} boxes number of boxes, 0 is no limit.. default 0
31228  * @cfg {Boolean} multiple multiple upload default true
31229  * @cfg {Number} thumbSize default 300
31230  * @cfg {String} fieldLabel
31231  * @cfg {Number} labelWidth default 4
31232  * @cfg {String} labelAlign (left|top) default left
31233  * @cfg {Boolean} editable (true|false) allow edit when upload a image default true
31234 * @cfg {Number} labellg set the width of label (1-12)
31235  * @cfg {Number} labelmd set the width of label (1-12)
31236  * @cfg {Number} labelsm set the width of label (1-12)
31237  * @cfg {Number} labelxs set the width of label (1-12)
31238  * 
31239  * @constructor
31240  * Create a new DocumentManager
31241  * @param {Object} config The config object
31242  */
31243
31244 Roo.bootstrap.DocumentManager = function(config){
31245     Roo.bootstrap.DocumentManager.superclass.constructor.call(this, config);
31246     
31247     this.files = [];
31248     this.delegates = [];
31249     
31250     this.addEvents({
31251         /**
31252          * @event initial
31253          * Fire when initial the DocumentManager
31254          * @param {Roo.bootstrap.DocumentManager} this
31255          */
31256         "initial" : true,
31257         /**
31258          * @event inspect
31259          * inspect selected file
31260          * @param {Roo.bootstrap.DocumentManager} this
31261          * @param {File} file
31262          */
31263         "inspect" : true,
31264         /**
31265          * @event exception
31266          * Fire when xhr load exception
31267          * @param {Roo.bootstrap.DocumentManager} this
31268          * @param {XMLHttpRequest} xhr
31269          */
31270         "exception" : true,
31271         /**
31272          * @event afterupload
31273          * Fire when xhr load exception
31274          * @param {Roo.bootstrap.DocumentManager} this
31275          * @param {XMLHttpRequest} xhr
31276          */
31277         "afterupload" : true,
31278         /**
31279          * @event prepare
31280          * prepare the form data
31281          * @param {Roo.bootstrap.DocumentManager} this
31282          * @param {Object} formData
31283          */
31284         "prepare" : true,
31285         /**
31286          * @event remove
31287          * Fire when remove the file
31288          * @param {Roo.bootstrap.DocumentManager} this
31289          * @param {Object} file
31290          */
31291         "remove" : true,
31292         /**
31293          * @event refresh
31294          * Fire after refresh the file
31295          * @param {Roo.bootstrap.DocumentManager} this
31296          */
31297         "refresh" : true,
31298         /**
31299          * @event click
31300          * Fire after click the image
31301          * @param {Roo.bootstrap.DocumentManager} this
31302          * @param {Object} file
31303          */
31304         "click" : true,
31305         /**
31306          * @event edit
31307          * Fire when upload a image and editable set to true
31308          * @param {Roo.bootstrap.DocumentManager} this
31309          * @param {Object} file
31310          */
31311         "edit" : true,
31312         /**
31313          * @event beforeselectfile
31314          * Fire before select file
31315          * @param {Roo.bootstrap.DocumentManager} this
31316          */
31317         "beforeselectfile" : true,
31318         /**
31319          * @event process
31320          * Fire before process file
31321          * @param {Roo.bootstrap.DocumentManager} this
31322          * @param {Object} file
31323          */
31324         "process" : true,
31325         /**
31326          * @event previewrendered
31327          * Fire when preview rendered
31328          * @param {Roo.bootstrap.DocumentManager} this
31329          * @param {Object} file
31330          */
31331         "previewrendered" : true,
31332         /**
31333          */
31334         "previewResize" : true
31335         
31336     });
31337 };
31338
31339 Roo.extend(Roo.bootstrap.DocumentManager, Roo.bootstrap.Component,  {
31340     
31341     boxes : 0,
31342     inputName : '',
31343     thumbSize : 300,
31344     multiple : true,
31345     files : false,
31346     method : 'POST',
31347     url : '',
31348     paramName : 'imageUpload',
31349     toolTipName : 'filename',
31350     fieldLabel : '',
31351     labelWidth : 4,
31352     labelAlign : 'left',
31353     editable : true,
31354     delegates : false,
31355     xhr : false, 
31356     
31357     labellg : 0,
31358     labelmd : 0,
31359     labelsm : 0,
31360     labelxs : 0,
31361     
31362     getAutoCreate : function()
31363     {   
31364         var managerWidget = {
31365             tag : 'div',
31366             cls : 'roo-document-manager',
31367             cn : [
31368                 {
31369                     tag : 'input',
31370                     cls : 'roo-document-manager-selector',
31371                     type : 'file'
31372                 },
31373                 {
31374                     tag : 'div',
31375                     cls : 'roo-document-manager-uploader',
31376                     cn : [
31377                         {
31378                             tag : 'div',
31379                             cls : 'roo-document-manager-upload-btn',
31380                             html : '<i class="fa fa-plus"></i>'
31381                         }
31382                     ]
31383                     
31384                 }
31385             ]
31386         };
31387         
31388         var content = [
31389             {
31390                 tag : 'div',
31391                 cls : 'column col-md-12',
31392                 cn : managerWidget
31393             }
31394         ];
31395         
31396         if(this.fieldLabel.length){
31397             
31398             content = [
31399                 {
31400                     tag : 'div',
31401                     cls : 'column col-md-12',
31402                     html : this.fieldLabel
31403                 },
31404                 {
31405                     tag : 'div',
31406                     cls : 'column col-md-12',
31407                     cn : managerWidget
31408                 }
31409             ];
31410
31411             if(this.labelAlign == 'left'){
31412                 content = [
31413                     {
31414                         tag : 'div',
31415                         cls : 'column',
31416                         html : this.fieldLabel
31417                     },
31418                     {
31419                         tag : 'div',
31420                         cls : 'column',
31421                         cn : managerWidget
31422                     }
31423                 ];
31424                 
31425                 if(this.labelWidth > 12){
31426                     content[0].style = "width: " + this.labelWidth + 'px';
31427                 }
31428
31429                 if(this.labelWidth < 13 && this.labelmd == 0){
31430                     this.labelmd = this.labelWidth;
31431                 }
31432
31433                 if(this.labellg > 0){
31434                     content[0].cls += ' col-lg-' + this.labellg;
31435                     content[1].cls += ' col-lg-' + (12 - this.labellg);
31436                 }
31437
31438                 if(this.labelmd > 0){
31439                     content[0].cls += ' col-md-' + this.labelmd;
31440                     content[1].cls += ' col-md-' + (12 - this.labelmd);
31441                 }
31442
31443                 if(this.labelsm > 0){
31444                     content[0].cls += ' col-sm-' + this.labelsm;
31445                     content[1].cls += ' col-sm-' + (12 - this.labelsm);
31446                 }
31447
31448                 if(this.labelxs > 0){
31449                     content[0].cls += ' col-xs-' + this.labelxs;
31450                     content[1].cls += ' col-xs-' + (12 - this.labelxs);
31451                 }
31452                 
31453             }
31454         }
31455         
31456         var cfg = {
31457             tag : 'div',
31458             cls : 'row clearfix',
31459             cn : content
31460         };
31461         
31462         return cfg;
31463         
31464     },
31465     
31466     initEvents : function()
31467     {
31468         this.managerEl = this.el.select('.roo-document-manager', true).first();
31469         this.managerEl.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31470         
31471         this.selectorEl = this.el.select('.roo-document-manager-selector', true).first();
31472         this.selectorEl.hide();
31473         
31474         if(this.multiple){
31475             this.selectorEl.attr('multiple', 'multiple');
31476         }
31477         
31478         this.selectorEl.on('change', this.onFileSelected, this);
31479         
31480         this.uploader = this.el.select('.roo-document-manager-uploader', true).first();
31481         this.uploader.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31482         
31483         this.uploader.on('click', this.onUploaderClick, this);
31484         
31485         this.renderProgressDialog();
31486         
31487         var _this = this;
31488         
31489         window.addEventListener("resize", function() { _this.refresh(); } );
31490         
31491         this.fireEvent('initial', this);
31492     },
31493     
31494     renderProgressDialog : function()
31495     {
31496         var _this = this;
31497         
31498         this.progressDialog = new Roo.bootstrap.Modal({
31499             cls : 'roo-document-manager-progress-dialog',
31500             allow_close : false,
31501             animate : false,
31502             title : '',
31503             buttons : [
31504                 {
31505                     name  :'cancel',
31506                     weight : 'danger',
31507                     html : 'Cancel'
31508                 }
31509             ], 
31510             listeners : { 
31511                 btnclick : function() {
31512                     _this.uploadCancel();
31513                     this.hide();
31514                 }
31515             }
31516         });
31517          
31518         this.progressDialog.render(Roo.get(document.body));
31519          
31520         this.progress = new Roo.bootstrap.Progress({
31521             cls : 'roo-document-manager-progress',
31522             active : true,
31523             striped : true
31524         });
31525         
31526         this.progress.render(this.progressDialog.getChildContainer());
31527         
31528         this.progressBar = new Roo.bootstrap.ProgressBar({
31529             cls : 'roo-document-manager-progress-bar',
31530             aria_valuenow : 0,
31531             aria_valuemin : 0,
31532             aria_valuemax : 12,
31533             panel : 'success'
31534         });
31535         
31536         this.progressBar.render(this.progress.getChildContainer());
31537     },
31538     
31539     onUploaderClick : function(e)
31540     {
31541         e.preventDefault();
31542      
31543         if(this.fireEvent('beforeselectfile', this) != false){
31544             this.selectorEl.dom.click();
31545         }
31546         
31547     },
31548     
31549     onFileSelected : function(e)
31550     {
31551         e.preventDefault();
31552         
31553         if(typeof(this.selectorEl.dom.files) == 'undefined' || !this.selectorEl.dom.files.length){
31554             return;
31555         }
31556         
31557         Roo.each(this.selectorEl.dom.files, function(file){
31558             if(this.fireEvent('inspect', this, file) != false){
31559                 this.files.push(file);
31560             }
31561         }, this);
31562         
31563         this.queue();
31564         
31565     },
31566     
31567     queue : function()
31568     {
31569         this.selectorEl.dom.value = '';
31570         
31571         if(!this.files || !this.files.length){
31572             return;
31573         }
31574         
31575         if(this.boxes > 0 && this.files.length > this.boxes){
31576             this.files = this.files.slice(0, this.boxes);
31577         }
31578         
31579         this.uploader.show();
31580         
31581         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31582             this.uploader.hide();
31583         }
31584         
31585         var _this = this;
31586         
31587         var files = [];
31588         
31589         var docs = [];
31590         
31591         Roo.each(this.files, function(file){
31592             
31593             if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31594                 var f = this.renderPreview(file);
31595                 files.push(f);
31596                 return;
31597             }
31598             
31599             if(file.type.indexOf('image') != -1){
31600                 this.delegates.push(
31601                     (function(){
31602                         _this.process(file);
31603                     }).createDelegate(this)
31604                 );
31605         
31606                 return;
31607             }
31608             
31609             docs.push(
31610                 (function(){
31611                     _this.process(file);
31612                 }).createDelegate(this)
31613             );
31614             
31615         }, this);
31616         
31617         this.files = files;
31618         
31619         this.delegates = this.delegates.concat(docs);
31620         
31621         if(!this.delegates.length){
31622             this.refresh();
31623             return;
31624         }
31625         
31626         this.progressBar.aria_valuemax = this.delegates.length;
31627         
31628         this.arrange();
31629         
31630         return;
31631     },
31632     
31633     arrange : function()
31634     {
31635         if(!this.delegates.length){
31636             this.progressDialog.hide();
31637             this.refresh();
31638             return;
31639         }
31640         
31641         var delegate = this.delegates.shift();
31642         
31643         this.progressDialog.show();
31644         
31645         this.progressDialog.setTitle((this.progressBar.aria_valuemax - this.delegates.length) + ' / ' + this.progressBar.aria_valuemax);
31646         
31647         this.progressBar.update(this.progressBar.aria_valuemax - this.delegates.length);
31648         
31649         delegate();
31650     },
31651     
31652     refresh : function()
31653     {
31654         this.uploader.show();
31655         
31656         if(this.boxes > 0 && this.files.length > this.boxes - 1){
31657             this.uploader.hide();
31658         }
31659         
31660         Roo.isTouch ? this.closable(false) : this.closable(true);
31661         
31662         this.fireEvent('refresh', this);
31663     },
31664     
31665     onRemove : function(e, el, o)
31666     {
31667         e.preventDefault();
31668         
31669         this.fireEvent('remove', this, o);
31670         
31671     },
31672     
31673     remove : function(o)
31674     {
31675         var files = [];
31676         
31677         Roo.each(this.files, function(file){
31678             if(typeof(file.id) == 'undefined' || file.id * 1 < 1 || file.id != o.id){
31679                 files.push(file);
31680                 return;
31681             }
31682
31683             o.target.remove();
31684
31685         }, this);
31686         
31687         this.files = files;
31688         
31689         this.refresh();
31690     },
31691     
31692     clear : function()
31693     {
31694         Roo.each(this.files, function(file){
31695             if(!file.target){
31696                 return;
31697             }
31698             
31699             file.target.remove();
31700
31701         }, this);
31702         
31703         this.files = [];
31704         
31705         this.refresh();
31706     },
31707     
31708     onClick : function(e, el, o)
31709     {
31710         e.preventDefault();
31711         
31712         this.fireEvent('click', this, o);
31713         
31714     },
31715     
31716     closable : function(closable)
31717     {
31718         Roo.each(this.managerEl.select('.roo-document-manager-preview > button.close', true).elements, function(el){
31719             
31720             el.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
31721             
31722             if(closable){
31723                 el.show();
31724                 return;
31725             }
31726             
31727             el.hide();
31728             
31729         }, this);
31730     },
31731     
31732     xhrOnLoad : function(xhr)
31733     {
31734         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31735             el.remove();
31736         }, this);
31737         
31738         if (xhr.readyState !== 4) {
31739             this.arrange();
31740             this.fireEvent('exception', this, xhr);
31741             return;
31742         }
31743
31744         var response = Roo.decode(xhr.responseText);
31745         
31746         if(!response.success){
31747             this.arrange();
31748             this.fireEvent('exception', this, xhr);
31749             return;
31750         }
31751         
31752         var file = this.renderPreview(response.data);
31753         
31754         this.files.push(file);
31755         
31756         this.arrange();
31757         
31758         this.fireEvent('afterupload', this, xhr);
31759         
31760     },
31761     
31762     xhrOnError : function(xhr)
31763     {
31764         Roo.log('xhr on error');
31765         
31766         var response = Roo.decode(xhr.responseText);
31767           
31768         Roo.log(response);
31769         
31770         this.arrange();
31771     },
31772     
31773     process : function(file)
31774     {
31775         if(this.fireEvent('process', this, file) !== false){
31776             if(this.editable && file.type.indexOf('image') != -1){
31777                 this.fireEvent('edit', this, file);
31778                 return;
31779             }
31780
31781             this.uploadStart(file, false);
31782
31783             return;
31784         }
31785         
31786     },
31787     
31788     uploadStart : function(file, crop)
31789     {
31790         this.xhr = new XMLHttpRequest();
31791         
31792         if(typeof(file.id) != 'undefined' && file.id * 1 > 0){
31793             this.arrange();
31794             return;
31795         }
31796         
31797         file.xhr = this.xhr;
31798             
31799         this.managerEl.createChild({
31800             tag : 'div',
31801             cls : 'roo-document-manager-loading',
31802             cn : [
31803                 {
31804                     tag : 'div',
31805                     tooltip : file.name,
31806                     cls : 'roo-document-manager-thumb',
31807                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31808                 }
31809             ]
31810
31811         });
31812
31813         this.xhr.open(this.method, this.url, true);
31814         
31815         var headers = {
31816             "Accept": "application/json",
31817             "Cache-Control": "no-cache",
31818             "X-Requested-With": "XMLHttpRequest"
31819         };
31820         
31821         for (var headerName in headers) {
31822             var headerValue = headers[headerName];
31823             if (headerValue) {
31824                 this.xhr.setRequestHeader(headerName, headerValue);
31825             }
31826         }
31827         
31828         var _this = this;
31829         
31830         this.xhr.onload = function()
31831         {
31832             _this.xhrOnLoad(_this.xhr);
31833         }
31834         
31835         this.xhr.onerror = function()
31836         {
31837             _this.xhrOnError(_this.xhr);
31838         }
31839         
31840         var formData = new FormData();
31841
31842         formData.append('returnHTML', 'NO');
31843         
31844         if(crop){
31845             formData.append('crop', crop);
31846         }
31847         
31848         formData.append(this.paramName, file, file.name);
31849         
31850         var options = {
31851             file : file, 
31852             manually : false
31853         };
31854         
31855         if(this.fireEvent('prepare', this, formData, options) != false){
31856             
31857             if(options.manually){
31858                 return;
31859             }
31860             
31861             this.xhr.send(formData);
31862             return;
31863         };
31864         
31865         this.uploadCancel();
31866     },
31867     
31868     uploadCancel : function()
31869     {
31870         if (this.xhr) {
31871             this.xhr.abort();
31872         }
31873         
31874         this.delegates = [];
31875         
31876         Roo.each(this.managerEl.select('.roo-document-manager-loading', true).elements, function(el){
31877             el.remove();
31878         }, this);
31879         
31880         this.arrange();
31881     },
31882     
31883     renderPreview : function(file)
31884     {
31885         if(typeof(file.target) != 'undefined' && file.target){
31886             return file;
31887         }
31888         
31889         var img_src = encodeURI(baseURL +'/Images/Thumb/' + this.thumbSize + '/' + file.id + '/' + file.filename);
31890         
31891         var previewEl = this.managerEl.createChild({
31892             tag : 'div',
31893             cls : 'roo-document-manager-preview',
31894             cn : [
31895                 {
31896                     tag : 'div',
31897                     tooltip : file[this.toolTipName],
31898                     cls : 'roo-document-manager-thumb',
31899                     html : '<img tooltip="' + file[this.toolTipName] + '" src="' + img_src + '">'
31900                 },
31901                 {
31902                     tag : 'button',
31903                     cls : 'close',
31904                     html : '<i class="fa fa-times-circle"></i>'
31905                 }
31906             ]
31907         });
31908
31909         var close = previewEl.select('button.close', true).first();
31910
31911         close.on('click', this.onRemove, this, file);
31912
31913         file.target = previewEl;
31914
31915         var image = previewEl.select('img', true).first();
31916         
31917         var _this = this;
31918         
31919         image.dom.addEventListener("load", function(){ _this.onPreviewLoad(file, image); });
31920         
31921         image.on('click', this.onClick, this, file);
31922         
31923         this.fireEvent('previewrendered', this, file);
31924         
31925         return file;
31926         
31927     },
31928     
31929     onPreviewLoad : function(file, image)
31930     {
31931         if(typeof(file.target) == 'undefined' || !file.target){
31932             return;
31933         }
31934         
31935         var width = image.dom.naturalWidth || image.dom.width;
31936         var height = image.dom.naturalHeight || image.dom.height;
31937         
31938         if(!this.previewResize) {
31939             return;
31940         }
31941         
31942         if(width > height){
31943             file.target.addClass('wide');
31944             return;
31945         }
31946         
31947         file.target.addClass('tall');
31948         return;
31949         
31950     },
31951     
31952     uploadFromSource : function(file, crop)
31953     {
31954         this.xhr = new XMLHttpRequest();
31955         
31956         this.managerEl.createChild({
31957             tag : 'div',
31958             cls : 'roo-document-manager-loading',
31959             cn : [
31960                 {
31961                     tag : 'div',
31962                     tooltip : file.name,
31963                     cls : 'roo-document-manager-thumb',
31964                     html : '<i class="fa fa-circle-o-notch fa-spin"></i>'
31965                 }
31966             ]
31967
31968         });
31969
31970         this.xhr.open(this.method, this.url, true);
31971         
31972         var headers = {
31973             "Accept": "application/json",
31974             "Cache-Control": "no-cache",
31975             "X-Requested-With": "XMLHttpRequest"
31976         };
31977         
31978         for (var headerName in headers) {
31979             var headerValue = headers[headerName];
31980             if (headerValue) {
31981                 this.xhr.setRequestHeader(headerName, headerValue);
31982             }
31983         }
31984         
31985         var _this = this;
31986         
31987         this.xhr.onload = function()
31988         {
31989             _this.xhrOnLoad(_this.xhr);
31990         }
31991         
31992         this.xhr.onerror = function()
31993         {
31994             _this.xhrOnError(_this.xhr);
31995         }
31996         
31997         var formData = new FormData();
31998
31999         formData.append('returnHTML', 'NO');
32000         
32001         formData.append('crop', crop);
32002         
32003         if(typeof(file.filename) != 'undefined'){
32004             formData.append('filename', file.filename);
32005         }
32006         
32007         if(typeof(file.mimetype) != 'undefined'){
32008             formData.append('mimetype', file.mimetype);
32009         }
32010         
32011         Roo.log(formData);
32012         
32013         if(this.fireEvent('prepare', this, formData) != false){
32014             this.xhr.send(formData);
32015         };
32016     }
32017 });
32018
32019 /*
32020 * Licence: LGPL
32021 */
32022
32023 /**
32024  * @class Roo.bootstrap.DocumentViewer
32025  * @extends Roo.bootstrap.Component
32026  * Bootstrap DocumentViewer class
32027  * @cfg {Boolean} showDownload (true|false) show download button (default true)
32028  * @cfg {Boolean} showTrash (true|false) show trash button (default true)
32029  * 
32030  * @constructor
32031  * Create a new DocumentViewer
32032  * @param {Object} config The config object
32033  */
32034
32035 Roo.bootstrap.DocumentViewer = function(config){
32036     Roo.bootstrap.DocumentViewer.superclass.constructor.call(this, config);
32037     
32038     this.addEvents({
32039         /**
32040          * @event initial
32041          * Fire after initEvent
32042          * @param {Roo.bootstrap.DocumentViewer} this
32043          */
32044         "initial" : true,
32045         /**
32046          * @event click
32047          * Fire after click
32048          * @param {Roo.bootstrap.DocumentViewer} this
32049          */
32050         "click" : true,
32051         /**
32052          * @event download
32053          * Fire after download button
32054          * @param {Roo.bootstrap.DocumentViewer} this
32055          */
32056         "download" : true,
32057         /**
32058          * @event trash
32059          * Fire after trash button
32060          * @param {Roo.bootstrap.DocumentViewer} this
32061          */
32062         "trash" : true
32063         
32064     });
32065 };
32066
32067 Roo.extend(Roo.bootstrap.DocumentViewer, Roo.bootstrap.Component,  {
32068     
32069     showDownload : true,
32070     
32071     showTrash : true,
32072     
32073     getAutoCreate : function()
32074     {
32075         var cfg = {
32076             tag : 'div',
32077             cls : 'roo-document-viewer',
32078             cn : [
32079                 {
32080                     tag : 'div',
32081                     cls : 'roo-document-viewer-body',
32082                     cn : [
32083                         {
32084                             tag : 'div',
32085                             cls : 'roo-document-viewer-thumb',
32086                             cn : [
32087                                 {
32088                                     tag : 'img',
32089                                     cls : 'roo-document-viewer-image'
32090                                 }
32091                             ]
32092                         }
32093                     ]
32094                 },
32095                 {
32096                     tag : 'div',
32097                     cls : 'roo-document-viewer-footer',
32098                     cn : {
32099                         tag : 'div',
32100                         cls : 'btn-group btn-group-justified roo-document-viewer-btn-group',
32101                         cn : [
32102                             {
32103                                 tag : 'div',
32104                                 cls : 'btn-group roo-document-viewer-download',
32105                                 cn : [
32106                                     {
32107                                         tag : 'button',
32108                                         cls : 'btn btn-default',
32109                                         html : '<i class="fa fa-download"></i>'
32110                                     }
32111                                 ]
32112                             },
32113                             {
32114                                 tag : 'div',
32115                                 cls : 'btn-group roo-document-viewer-trash',
32116                                 cn : [
32117                                     {
32118                                         tag : 'button',
32119                                         cls : 'btn btn-default',
32120                                         html : '<i class="fa fa-trash"></i>'
32121                                     }
32122                                 ]
32123                             }
32124                         ]
32125                     }
32126                 }
32127             ]
32128         };
32129         
32130         return cfg;
32131     },
32132     
32133     initEvents : function()
32134     {
32135         this.bodyEl = this.el.select('.roo-document-viewer-body', true).first();
32136         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
32137         
32138         this.thumbEl = this.el.select('.roo-document-viewer-thumb', true).first();
32139         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
32140         
32141         this.imageEl = this.el.select('.roo-document-viewer-image', true).first();
32142         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
32143         
32144         this.footerEl = this.el.select('.roo-document-viewer-footer', true).first();
32145         this.footerEl.setVisibilityMode(Roo.Element.DISPLAY);
32146         
32147         this.downloadBtn = this.el.select('.roo-document-viewer-download', true).first();
32148         this.downloadBtn.setVisibilityMode(Roo.Element.DISPLAY);
32149         
32150         this.trashBtn = this.el.select('.roo-document-viewer-trash', true).first();
32151         this.trashBtn.setVisibilityMode(Roo.Element.DISPLAY);
32152         
32153         this.bodyEl.on('click', this.onClick, this);
32154         this.downloadBtn.on('click', this.onDownload, this);
32155         this.trashBtn.on('click', this.onTrash, this);
32156         
32157         this.downloadBtn.hide();
32158         this.trashBtn.hide();
32159         
32160         if(this.showDownload){
32161             this.downloadBtn.show();
32162         }
32163         
32164         if(this.showTrash){
32165             this.trashBtn.show();
32166         }
32167         
32168         if(!this.showDownload && !this.showTrash) {
32169             this.footerEl.hide();
32170         }
32171         
32172     },
32173     
32174     initial : function()
32175     {
32176         this.fireEvent('initial', this);
32177         
32178     },
32179     
32180     onClick : function(e)
32181     {
32182         e.preventDefault();
32183         
32184         this.fireEvent('click', this);
32185     },
32186     
32187     onDownload : function(e)
32188     {
32189         e.preventDefault();
32190         
32191         this.fireEvent('download', this);
32192     },
32193     
32194     onTrash : function(e)
32195     {
32196         e.preventDefault();
32197         
32198         this.fireEvent('trash', this);
32199     }
32200     
32201 });
32202 /*
32203  * - LGPL
32204  *
32205  * nav progress bar
32206  * 
32207  */
32208
32209 /**
32210  * @class Roo.bootstrap.NavProgressBar
32211  * @extends Roo.bootstrap.Component
32212  * Bootstrap NavProgressBar class
32213  * 
32214  * @constructor
32215  * Create a new nav progress bar
32216  * @param {Object} config The config object
32217  */
32218
32219 Roo.bootstrap.NavProgressBar = function(config){
32220     Roo.bootstrap.NavProgressBar.superclass.constructor.call(this, config);
32221
32222     this.bullets = this.bullets || [];
32223    
32224 //    Roo.bootstrap.NavProgressBar.register(this);
32225      this.addEvents({
32226         /**
32227              * @event changed
32228              * Fires when the active item changes
32229              * @param {Roo.bootstrap.NavProgressBar} this
32230              * @param {Roo.bootstrap.NavProgressItem} selected The item selected
32231              * @param {Roo.bootstrap.NavProgressItem} prev The previously selected item 
32232          */
32233         'changed': true
32234      });
32235     
32236 };
32237
32238 Roo.extend(Roo.bootstrap.NavProgressBar, Roo.bootstrap.Component,  {
32239     
32240     bullets : [],
32241     barItems : [],
32242     
32243     getAutoCreate : function()
32244     {
32245         var cfg = Roo.apply({}, Roo.bootstrap.NavProgressBar.superclass.getAutoCreate.call(this));
32246         
32247         cfg = {
32248             tag : 'div',
32249             cls : 'roo-navigation-bar-group',
32250             cn : [
32251                 {
32252                     tag : 'div',
32253                     cls : 'roo-navigation-top-bar'
32254                 },
32255                 {
32256                     tag : 'div',
32257                     cls : 'roo-navigation-bullets-bar',
32258                     cn : [
32259                         {
32260                             tag : 'ul',
32261                             cls : 'roo-navigation-bar'
32262                         }
32263                     ]
32264                 },
32265                 
32266                 {
32267                     tag : 'div',
32268                     cls : 'roo-navigation-bottom-bar'
32269                 }
32270             ]
32271             
32272         };
32273         
32274         return cfg;
32275         
32276     },
32277     
32278     initEvents: function() 
32279     {
32280         
32281     },
32282     
32283     onRender : function(ct, position) 
32284     {
32285         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32286         
32287         if(this.bullets.length){
32288             Roo.each(this.bullets, function(b){
32289                this.addItem(b);
32290             }, this);
32291         }
32292         
32293         this.format();
32294         
32295     },
32296     
32297     addItem : function(cfg)
32298     {
32299         var item = new Roo.bootstrap.NavProgressItem(cfg);
32300         
32301         item.parentId = this.id;
32302         item.render(this.el.select('.roo-navigation-bar', true).first(), null);
32303         
32304         if(cfg.html){
32305             var top = new Roo.bootstrap.Element({
32306                 tag : 'div',
32307                 cls : 'roo-navigation-bar-text'
32308             });
32309             
32310             var bottom = new Roo.bootstrap.Element({
32311                 tag : 'div',
32312                 cls : 'roo-navigation-bar-text'
32313             });
32314             
32315             top.onRender(this.el.select('.roo-navigation-top-bar', true).first(), null);
32316             bottom.onRender(this.el.select('.roo-navigation-bottom-bar', true).first(), null);
32317             
32318             var topText = new Roo.bootstrap.Element({
32319                 tag : 'span',
32320                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? cfg.html : ''
32321             });
32322             
32323             var bottomText = new Roo.bootstrap.Element({
32324                 tag : 'span',
32325                 html : (typeof(cfg.position) != 'undefined' && cfg.position == 'top') ? '' : cfg.html
32326             });
32327             
32328             topText.onRender(top.el, null);
32329             bottomText.onRender(bottom.el, null);
32330             
32331             item.topEl = top;
32332             item.bottomEl = bottom;
32333         }
32334         
32335         this.barItems.push(item);
32336         
32337         return item;
32338     },
32339     
32340     getActive : function()
32341     {
32342         var active = false;
32343         
32344         Roo.each(this.barItems, function(v){
32345             
32346             if (!v.isActive()) {
32347                 return;
32348             }
32349             
32350             active = v;
32351             return false;
32352             
32353         });
32354         
32355         return active;
32356     },
32357     
32358     setActiveItem : function(item)
32359     {
32360         var prev = false;
32361         
32362         Roo.each(this.barItems, function(v){
32363             if (v.rid == item.rid) {
32364                 return ;
32365             }
32366             
32367             if (v.isActive()) {
32368                 v.setActive(false);
32369                 prev = v;
32370             }
32371         });
32372
32373         item.setActive(true);
32374         
32375         this.fireEvent('changed', this, item, prev);
32376     },
32377     
32378     getBarItem: function(rid)
32379     {
32380         var ret = false;
32381         
32382         Roo.each(this.barItems, function(e) {
32383             if (e.rid != rid) {
32384                 return;
32385             }
32386             
32387             ret =  e;
32388             return false;
32389         });
32390         
32391         return ret;
32392     },
32393     
32394     indexOfItem : function(item)
32395     {
32396         var index = false;
32397         
32398         Roo.each(this.barItems, function(v, i){
32399             
32400             if (v.rid != item.rid) {
32401                 return;
32402             }
32403             
32404             index = i;
32405             return false
32406         });
32407         
32408         return index;
32409     },
32410     
32411     setActiveNext : function()
32412     {
32413         var i = this.indexOfItem(this.getActive());
32414         
32415         if (i > this.barItems.length) {
32416             return;
32417         }
32418         
32419         this.setActiveItem(this.barItems[i+1]);
32420     },
32421     
32422     setActivePrev : function()
32423     {
32424         var i = this.indexOfItem(this.getActive());
32425         
32426         if (i  < 1) {
32427             return;
32428         }
32429         
32430         this.setActiveItem(this.barItems[i-1]);
32431     },
32432     
32433     format : function()
32434     {
32435         if(!this.barItems.length){
32436             return;
32437         }
32438      
32439         var width = 100 / this.barItems.length;
32440         
32441         Roo.each(this.barItems, function(i){
32442             i.el.setStyle('width', width + '%');
32443             i.topEl.el.setStyle('width', width + '%');
32444             i.bottomEl.el.setStyle('width', width + '%');
32445         }, this);
32446         
32447     }
32448     
32449 });
32450 /*
32451  * - LGPL
32452  *
32453  * Nav Progress Item
32454  * 
32455  */
32456
32457 /**
32458  * @class Roo.bootstrap.NavProgressItem
32459  * @extends Roo.bootstrap.Component
32460  * Bootstrap NavProgressItem class
32461  * @cfg {String} rid the reference id
32462  * @cfg {Boolean} active (true|false) Is item active default false
32463  * @cfg {Boolean} disabled (true|false) Is item active default false
32464  * @cfg {String} html
32465  * @cfg {String} position (top|bottom) text position default bottom
32466  * @cfg {String} icon show icon instead of number
32467  * 
32468  * @constructor
32469  * Create a new NavProgressItem
32470  * @param {Object} config The config object
32471  */
32472 Roo.bootstrap.NavProgressItem = function(config){
32473     Roo.bootstrap.NavProgressItem.superclass.constructor.call(this, config);
32474     this.addEvents({
32475         // raw events
32476         /**
32477          * @event click
32478          * The raw click event for the entire grid.
32479          * @param {Roo.bootstrap.NavProgressItem} this
32480          * @param {Roo.EventObject} e
32481          */
32482         "click" : true
32483     });
32484    
32485 };
32486
32487 Roo.extend(Roo.bootstrap.NavProgressItem, Roo.bootstrap.Component,  {
32488     
32489     rid : '',
32490     active : false,
32491     disabled : false,
32492     html : '',
32493     position : 'bottom',
32494     icon : false,
32495     
32496     getAutoCreate : function()
32497     {
32498         var iconCls = 'roo-navigation-bar-item-icon';
32499         
32500         iconCls += ((this.icon) ? (' ' + this.icon) : (' step-number')) ;
32501         
32502         var cfg = {
32503             tag: 'li',
32504             cls: 'roo-navigation-bar-item',
32505             cn : [
32506                 {
32507                     tag : 'i',
32508                     cls : iconCls
32509                 }
32510             ]
32511         };
32512         
32513         if(this.active){
32514             cfg.cls += ' active';
32515         }
32516         if(this.disabled){
32517             cfg.cls += ' disabled';
32518         }
32519         
32520         return cfg;
32521     },
32522     
32523     disable : function()
32524     {
32525         this.setDisabled(true);
32526     },
32527     
32528     enable : function()
32529     {
32530         this.setDisabled(false);
32531     },
32532     
32533     initEvents: function() 
32534     {
32535         this.iconEl = this.el.select('.roo-navigation-bar-item-icon', true).first();
32536         
32537         this.iconEl.on('click', this.onClick, this);
32538     },
32539     
32540     onClick : function(e)
32541     {
32542         e.preventDefault();
32543         
32544         if(this.disabled){
32545             return;
32546         }
32547         
32548         if(this.fireEvent('click', this, e) === false){
32549             return;
32550         };
32551         
32552         this.parent().setActiveItem(this);
32553     },
32554     
32555     isActive: function () 
32556     {
32557         return this.active;
32558     },
32559     
32560     setActive : function(state)
32561     {
32562         if(this.active == state){
32563             return;
32564         }
32565         
32566         this.active = state;
32567         
32568         if (state) {
32569             this.el.addClass('active');
32570             return;
32571         }
32572         
32573         this.el.removeClass('active');
32574         
32575         return;
32576     },
32577     
32578     setDisabled : function(state)
32579     {
32580         if(this.disabled == state){
32581             return;
32582         }
32583         
32584         this.disabled = state;
32585         
32586         if (state) {
32587             this.el.addClass('disabled');
32588             return;
32589         }
32590         
32591         this.el.removeClass('disabled');
32592     },
32593     
32594     tooltipEl : function()
32595     {
32596         return this.el.select('.roo-navigation-bar-item-icon', true).first();;
32597     }
32598 });
32599  
32600
32601  /*
32602  * - LGPL
32603  *
32604  * FieldLabel
32605  * 
32606  */
32607
32608 /**
32609  * @class Roo.bootstrap.FieldLabel
32610  * @extends Roo.bootstrap.Component
32611  * Bootstrap FieldLabel class
32612  * @cfg {String} html contents of the element
32613  * @cfg {String} tag tag of the element default label
32614  * @cfg {String} cls class of the element
32615  * @cfg {String} target label target 
32616  * @cfg {Boolean} allowBlank (true|false) target allowBlank default true
32617  * @cfg {String} invalidClass DEPRICATED - BS4 uses is-invalid
32618  * @cfg {String} validClass DEPRICATED - BS4 uses is-valid
32619  * @cfg {String} iconTooltip default "This field is required"
32620  * @cfg {String} indicatorpos (left|right) default left
32621  * 
32622  * @constructor
32623  * Create a new FieldLabel
32624  * @param {Object} config The config object
32625  */
32626
32627 Roo.bootstrap.FieldLabel = function(config){
32628     Roo.bootstrap.Element.superclass.constructor.call(this, config);
32629     
32630     this.addEvents({
32631             /**
32632              * @event invalid
32633              * Fires after the field has been marked as invalid.
32634              * @param {Roo.form.FieldLabel} this
32635              * @param {String} msg The validation message
32636              */
32637             invalid : true,
32638             /**
32639              * @event valid
32640              * Fires after the field has been validated with no errors.
32641              * @param {Roo.form.FieldLabel} this
32642              */
32643             valid : true
32644         });
32645 };
32646
32647 Roo.extend(Roo.bootstrap.FieldLabel, Roo.bootstrap.Component,  {
32648     
32649     tag: 'label',
32650     cls: '',
32651     html: '',
32652     target: '',
32653     allowBlank : true,
32654     invalidClass : 'has-warning',
32655     validClass : 'has-success',
32656     iconTooltip : 'This field is required',
32657     indicatorpos : 'left',
32658     
32659     getAutoCreate : function(){
32660         
32661         var cls = "";
32662         if (!this.allowBlank) {
32663             cls  = "visible";
32664         }
32665         
32666         var cfg = {
32667             tag : this.tag,
32668             cls : 'roo-bootstrap-field-label ' + this.cls,
32669             for : this.target,
32670             cn : [
32671                 {
32672                     tag : 'i',
32673                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star ' + cls,
32674                     tooltip : this.iconTooltip
32675                 },
32676                 {
32677                     tag : 'span',
32678                     html : this.html
32679                 }
32680             ] 
32681         };
32682         
32683         if(this.indicatorpos == 'right'){
32684             var cfg = {
32685                 tag : this.tag,
32686                 cls : 'roo-bootstrap-field-label ' + this.cls,
32687                 for : this.target,
32688                 cn : [
32689                     {
32690                         tag : 'span',
32691                         html : this.html
32692                     },
32693                     {
32694                         tag : 'i',
32695                         cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star '+ cls,
32696                         tooltip : this.iconTooltip
32697                     }
32698                 ] 
32699             };
32700         }
32701         
32702         return cfg;
32703     },
32704     
32705     initEvents: function() 
32706     {
32707         Roo.bootstrap.Element.superclass.initEvents.call(this);
32708         
32709         this.indicator = this.indicatorEl();
32710         
32711         if(this.indicator){
32712             this.indicator.removeClass('visible');
32713             this.indicator.addClass('invisible');
32714         }
32715         
32716         Roo.bootstrap.FieldLabel.register(this);
32717     },
32718     
32719     indicatorEl : function()
32720     {
32721         var indicator = this.el.select('i.roo-required-indicator',true).first();
32722         
32723         if(!indicator){
32724             return false;
32725         }
32726         
32727         return indicator;
32728         
32729     },
32730     
32731     /**
32732      * Mark this field as valid
32733      */
32734     markValid : function()
32735     {
32736         if(this.indicator){
32737             this.indicator.removeClass('visible');
32738             this.indicator.addClass('invisible');
32739         }
32740         if (Roo.bootstrap.version == 3) {
32741             this.el.removeClass(this.invalidClass);
32742             this.el.addClass(this.validClass);
32743         } else {
32744             this.el.removeClass('is-invalid');
32745             this.el.addClass('is-valid');
32746         }
32747         
32748         
32749         this.fireEvent('valid', this);
32750     },
32751     
32752     /**
32753      * Mark this field as invalid
32754      * @param {String} msg The validation message
32755      */
32756     markInvalid : function(msg)
32757     {
32758         if(this.indicator){
32759             this.indicator.removeClass('invisible');
32760             this.indicator.addClass('visible');
32761         }
32762           if (Roo.bootstrap.version == 3) {
32763             this.el.removeClass(this.validClass);
32764             this.el.addClass(this.invalidClass);
32765         } else {
32766             this.el.removeClass('is-valid');
32767             this.el.addClass('is-invalid');
32768         }
32769         
32770         
32771         this.fireEvent('invalid', this, msg);
32772     }
32773     
32774    
32775 });
32776
32777 Roo.apply(Roo.bootstrap.FieldLabel, {
32778     
32779     groups: {},
32780     
32781      /**
32782     * register a FieldLabel Group
32783     * @param {Roo.bootstrap.FieldLabel} the FieldLabel to add
32784     */
32785     register : function(label)
32786     {
32787         if(this.groups.hasOwnProperty(label.target)){
32788             return;
32789         }
32790      
32791         this.groups[label.target] = label;
32792         
32793     },
32794     /**
32795     * fetch a FieldLabel Group based on the target
32796     * @param {string} target
32797     * @returns {Roo.bootstrap.FieldLabel} the CheckBox group
32798     */
32799     get: function(target) {
32800         if (typeof(this.groups[target]) == 'undefined') {
32801             return false;
32802         }
32803         
32804         return this.groups[target] ;
32805     }
32806 });
32807
32808  
32809
32810  /*
32811  * - LGPL
32812  *
32813  * page DateSplitField.
32814  * 
32815  */
32816
32817
32818 /**
32819  * @class Roo.bootstrap.DateSplitField
32820  * @extends Roo.bootstrap.Component
32821  * Bootstrap DateSplitField class
32822  * @cfg {string} fieldLabel - the label associated
32823  * @cfg {Number} labelWidth set the width of label (0-12)
32824  * @cfg {String} labelAlign (top|left)
32825  * @cfg {Boolean} dayAllowBlank (true|false) default false
32826  * @cfg {Boolean} monthAllowBlank (true|false) default false
32827  * @cfg {Boolean} yearAllowBlank (true|false) default false
32828  * @cfg {string} dayPlaceholder 
32829  * @cfg {string} monthPlaceholder
32830  * @cfg {string} yearPlaceholder
32831  * @cfg {string} dayFormat default 'd'
32832  * @cfg {string} monthFormat default 'm'
32833  * @cfg {string} yearFormat default 'Y'
32834  * @cfg {Number} labellg set the width of label (1-12)
32835  * @cfg {Number} labelmd set the width of label (1-12)
32836  * @cfg {Number} labelsm set the width of label (1-12)
32837  * @cfg {Number} labelxs set the width of label (1-12)
32838
32839  *     
32840  * @constructor
32841  * Create a new DateSplitField
32842  * @param {Object} config The config object
32843  */
32844
32845 Roo.bootstrap.DateSplitField = function(config){
32846     Roo.bootstrap.DateSplitField.superclass.constructor.call(this, config);
32847     
32848     this.addEvents({
32849         // raw events
32850          /**
32851          * @event years
32852          * getting the data of years
32853          * @param {Roo.bootstrap.DateSplitField} this
32854          * @param {Object} years
32855          */
32856         "years" : true,
32857         /**
32858          * @event days
32859          * getting the data of days
32860          * @param {Roo.bootstrap.DateSplitField} this
32861          * @param {Object} days
32862          */
32863         "days" : true,
32864         /**
32865          * @event invalid
32866          * Fires after the field has been marked as invalid.
32867          * @param {Roo.form.Field} this
32868          * @param {String} msg The validation message
32869          */
32870         invalid : true,
32871        /**
32872          * @event valid
32873          * Fires after the field has been validated with no errors.
32874          * @param {Roo.form.Field} this
32875          */
32876         valid : true
32877     });
32878 };
32879
32880 Roo.extend(Roo.bootstrap.DateSplitField, Roo.bootstrap.Component,  {
32881     
32882     fieldLabel : '',
32883     labelAlign : 'top',
32884     labelWidth : 3,
32885     dayAllowBlank : false,
32886     monthAllowBlank : false,
32887     yearAllowBlank : false,
32888     dayPlaceholder : '',
32889     monthPlaceholder : '',
32890     yearPlaceholder : '',
32891     dayFormat : 'd',
32892     monthFormat : 'm',
32893     yearFormat : 'Y',
32894     isFormField : true,
32895     labellg : 0,
32896     labelmd : 0,
32897     labelsm : 0,
32898     labelxs : 0,
32899     
32900     getAutoCreate : function()
32901     {
32902         var cfg = {
32903             tag : 'div',
32904             cls : 'row roo-date-split-field-group',
32905             cn : [
32906                 {
32907                     tag : 'input',
32908                     type : 'hidden',
32909                     cls : 'form-hidden-field roo-date-split-field-group-value',
32910                     name : this.name
32911                 }
32912             ]
32913         };
32914         
32915         var labelCls = 'col-md-12';
32916         var contentCls = 'col-md-4';
32917         
32918         if(this.fieldLabel){
32919             
32920             var label = {
32921                 tag : 'div',
32922                 cls : 'column roo-date-split-field-label col-md-' + ((this.labelAlign == 'top') ? '12' : this.labelWidth),
32923                 cn : [
32924                     {
32925                         tag : 'label',
32926                         html : this.fieldLabel
32927                     }
32928                 ]
32929             };
32930             
32931             if(this.labelAlign == 'left'){
32932             
32933                 if(this.labelWidth > 12){
32934                     label.style = "width: " + this.labelWidth + 'px';
32935                 }
32936
32937                 if(this.labelWidth < 13 && this.labelmd == 0){
32938                     this.labelmd = this.labelWidth;
32939                 }
32940
32941                 if(this.labellg > 0){
32942                     labelCls = ' col-lg-' + this.labellg;
32943                     contentCls = ' col-lg-' + ((12 - this.labellg) / 3);
32944                 }
32945
32946                 if(this.labelmd > 0){
32947                     labelCls = ' col-md-' + this.labelmd;
32948                     contentCls = ' col-md-' + ((12 - this.labelmd) / 3);
32949                 }
32950
32951                 if(this.labelsm > 0){
32952                     labelCls = ' col-sm-' + this.labelsm;
32953                     contentCls = ' col-sm-' + ((12 - this.labelsm) / 3);
32954                 }
32955
32956                 if(this.labelxs > 0){
32957                     labelCls = ' col-xs-' + this.labelxs;
32958                     contentCls = ' col-xs-' + ((12 - this.labelxs) / 3);
32959                 }
32960             }
32961             
32962             label.cls += ' ' + labelCls;
32963             
32964             cfg.cn.push(label);
32965         }
32966         
32967         Roo.each(['day', 'month', 'year'], function(t){
32968             cfg.cn.push({
32969                 tag : 'div',
32970                 cls : 'column roo-date-split-field-' + t + ' ' + contentCls
32971             });
32972         }, this);
32973         
32974         return cfg;
32975     },
32976     
32977     inputEl: function ()
32978     {
32979         return this.el.select('.roo-date-split-field-group-value', true).first();
32980     },
32981     
32982     onRender : function(ct, position) 
32983     {
32984         var _this = this;
32985         
32986         Roo.bootstrap.NavProgressBar.superclass.onRender.call(this, ct, position);
32987         
32988         this.inputEl = this.el.select('.roo-date-split-field-group-value', true).first();
32989         
32990         this.dayField = new Roo.bootstrap.ComboBox({
32991             allowBlank : this.dayAllowBlank,
32992             alwaysQuery : true,
32993             displayField : 'value',
32994             editable : false,
32995             fieldLabel : '',
32996             forceSelection : true,
32997             mode : 'local',
32998             placeholder : this.dayPlaceholder,
32999             selectOnFocus : true,
33000             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33001             triggerAction : 'all',
33002             typeAhead : true,
33003             valueField : 'value',
33004             store : new Roo.data.SimpleStore({
33005                 data : (function() {    
33006                     var days = [];
33007                     _this.fireEvent('days', _this, days);
33008                     return days;
33009                 })(),
33010                 fields : [ 'value' ]
33011             }),
33012             listeners : {
33013                 select : function (_self, record, index)
33014                 {
33015                     _this.setValue(_this.getValue());
33016                 }
33017             }
33018         });
33019
33020         this.dayField.render(this.el.select('.roo-date-split-field-day', true).first(), null);
33021         
33022         this.monthField = new Roo.bootstrap.MonthField({
33023             after : '<i class=\"fa fa-calendar\"></i>',
33024             allowBlank : this.monthAllowBlank,
33025             placeholder : this.monthPlaceholder,
33026             readOnly : true,
33027             listeners : {
33028                 render : function (_self)
33029                 {
33030                     this.el.select('span.input-group-addon', true).first().on('click', function(e){
33031                         e.preventDefault();
33032                         _self.focus();
33033                     });
33034                 },
33035                 select : function (_self, oldvalue, newvalue)
33036                 {
33037                     _this.setValue(_this.getValue());
33038                 }
33039             }
33040         });
33041         
33042         this.monthField.render(this.el.select('.roo-date-split-field-month', true).first(), null);
33043         
33044         this.yearField = new Roo.bootstrap.ComboBox({
33045             allowBlank : this.yearAllowBlank,
33046             alwaysQuery : true,
33047             displayField : 'value',
33048             editable : false,
33049             fieldLabel : '',
33050             forceSelection : true,
33051             mode : 'local',
33052             placeholder : this.yearPlaceholder,
33053             selectOnFocus : true,
33054             tpl : '<div class="roo-select2-result"><b>{value}</b></div>',
33055             triggerAction : 'all',
33056             typeAhead : true,
33057             valueField : 'value',
33058             store : new Roo.data.SimpleStore({
33059                 data : (function() {
33060                     var years = [];
33061                     _this.fireEvent('years', _this, years);
33062                     return years;
33063                 })(),
33064                 fields : [ 'value' ]
33065             }),
33066             listeners : {
33067                 select : function (_self, record, index)
33068                 {
33069                     _this.setValue(_this.getValue());
33070                 }
33071             }
33072         });
33073
33074         this.yearField.render(this.el.select('.roo-date-split-field-year', true).first(), null);
33075     },
33076     
33077     setValue : function(v, format)
33078     {
33079         this.inputEl.dom.value = v;
33080         
33081         var f = format || (this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat);
33082         
33083         var d = Date.parseDate(v, f);
33084         
33085         if(!d){
33086             this.validate();
33087             return;
33088         }
33089         
33090         this.setDay(d.format(this.dayFormat));
33091         this.setMonth(d.format(this.monthFormat));
33092         this.setYear(d.format(this.yearFormat));
33093         
33094         this.validate();
33095         
33096         return;
33097     },
33098     
33099     setDay : function(v)
33100     {
33101         this.dayField.setValue(v);
33102         this.inputEl.dom.value = this.getValue();
33103         this.validate();
33104         return;
33105     },
33106     
33107     setMonth : function(v)
33108     {
33109         this.monthField.setValue(v, true);
33110         this.inputEl.dom.value = this.getValue();
33111         this.validate();
33112         return;
33113     },
33114     
33115     setYear : function(v)
33116     {
33117         this.yearField.setValue(v);
33118         this.inputEl.dom.value = this.getValue();
33119         this.validate();
33120         return;
33121     },
33122     
33123     getDay : function()
33124     {
33125         return this.dayField.getValue();
33126     },
33127     
33128     getMonth : function()
33129     {
33130         return this.monthField.getValue();
33131     },
33132     
33133     getYear : function()
33134     {
33135         return this.yearField.getValue();
33136     },
33137     
33138     getValue : function()
33139     {
33140         var f = this.yearFormat + '-' + this.monthFormat + '-' + this.dayFormat;
33141         
33142         var date = this.yearField.getValue() + '-' + this.monthField.getValue() + '-' + this.dayField.getValue();
33143         
33144         return date;
33145     },
33146     
33147     reset : function()
33148     {
33149         this.setDay('');
33150         this.setMonth('');
33151         this.setYear('');
33152         this.inputEl.dom.value = '';
33153         this.validate();
33154         return;
33155     },
33156     
33157     validate : function()
33158     {
33159         var d = this.dayField.validate();
33160         var m = this.monthField.validate();
33161         var y = this.yearField.validate();
33162         
33163         var valid = true;
33164         
33165         if(
33166                 (!this.dayAllowBlank && !d) ||
33167                 (!this.monthAllowBlank && !m) ||
33168                 (!this.yearAllowBlank && !y)
33169         ){
33170             valid = false;
33171         }
33172         
33173         if(this.dayAllowBlank && this.monthAllowBlank && this.yearAllowBlank){
33174             return valid;
33175         }
33176         
33177         if(valid){
33178             this.markValid();
33179             return valid;
33180         }
33181         
33182         this.markInvalid();
33183         
33184         return valid;
33185     },
33186     
33187     markValid : function()
33188     {
33189         
33190         var label = this.el.select('label', true).first();
33191         var icon = this.el.select('i.fa-star', true).first();
33192
33193         if(label && icon){
33194             icon.remove();
33195         }
33196         
33197         this.fireEvent('valid', this);
33198     },
33199     
33200      /**
33201      * Mark this field as invalid
33202      * @param {String} msg The validation message
33203      */
33204     markInvalid : function(msg)
33205     {
33206         
33207         var label = this.el.select('label', true).first();
33208         var icon = this.el.select('i.fa-star', true).first();
33209
33210         if(label && !icon){
33211             this.el.select('.roo-date-split-field-label', true).createChild({
33212                 tag : 'i',
33213                 cls : 'text-danger fa fa-lg fa-star',
33214                 tooltip : 'This field is required',
33215                 style : 'margin-right:5px;'
33216             }, label, true);
33217         }
33218         
33219         this.fireEvent('invalid', this, msg);
33220     },
33221     
33222     clearInvalid : function()
33223     {
33224         var label = this.el.select('label', true).first();
33225         var icon = this.el.select('i.fa-star', true).first();
33226
33227         if(label && icon){
33228             icon.remove();
33229         }
33230         
33231         this.fireEvent('valid', this);
33232     },
33233     
33234     getName: function()
33235     {
33236         return this.name;
33237     }
33238     
33239 });
33240
33241  /**
33242  *
33243  * This is based on 
33244  * http://masonry.desandro.com
33245  *
33246  * The idea is to render all the bricks based on vertical width...
33247  *
33248  * The original code extends 'outlayer' - we might need to use that....
33249  * 
33250  */
33251
33252
33253 /**
33254  * @class Roo.bootstrap.LayoutMasonry
33255  * @extends Roo.bootstrap.Component
33256  * Bootstrap Layout Masonry class
33257  * 
33258  * @constructor
33259  * Create a new Element
33260  * @param {Object} config The config object
33261  */
33262
33263 Roo.bootstrap.LayoutMasonry = function(config){
33264     
33265     Roo.bootstrap.LayoutMasonry.superclass.constructor.call(this, config);
33266     
33267     this.bricks = [];
33268     
33269     Roo.bootstrap.LayoutMasonry.register(this);
33270     
33271     this.addEvents({
33272         // raw events
33273         /**
33274          * @event layout
33275          * Fire after layout the items
33276          * @param {Roo.bootstrap.LayoutMasonry} this
33277          * @param {Roo.EventObject} e
33278          */
33279         "layout" : true
33280     });
33281     
33282 };
33283
33284 Roo.extend(Roo.bootstrap.LayoutMasonry, Roo.bootstrap.Component,  {
33285     
33286     /**
33287      * @cfg {Boolean} isLayoutInstant = no animation?
33288      */   
33289     isLayoutInstant : false, // needed?
33290    
33291     /**
33292      * @cfg {Number} boxWidth  width of the columns
33293      */   
33294     boxWidth : 450,
33295     
33296       /**
33297      * @cfg {Number} boxHeight  - 0 for square, or fix it at a certian height
33298      */   
33299     boxHeight : 0,
33300     
33301     /**
33302      * @cfg {Number} padWidth padding below box..
33303      */   
33304     padWidth : 10, 
33305     
33306     /**
33307      * @cfg {Number} gutter gutter width..
33308      */   
33309     gutter : 10,
33310     
33311      /**
33312      * @cfg {Number} maxCols maximum number of columns
33313      */   
33314     
33315     maxCols: 0,
33316     
33317     /**
33318      * @cfg {Boolean} isAutoInitial defalut true
33319      */   
33320     isAutoInitial : true, 
33321     
33322     containerWidth: 0,
33323     
33324     /**
33325      * @cfg {Boolean} isHorizontal defalut false
33326      */   
33327     isHorizontal : false, 
33328
33329     currentSize : null,
33330     
33331     tag: 'div',
33332     
33333     cls: '',
33334     
33335     bricks: null, //CompositeElement
33336     
33337     cols : 1,
33338     
33339     _isLayoutInited : false,
33340     
33341 //    isAlternative : false, // only use for vertical layout...
33342     
33343     /**
33344      * @cfg {Number} alternativePadWidth padding below box..
33345      */   
33346     alternativePadWidth : 50,
33347     
33348     selectedBrick : [],
33349     
33350     getAutoCreate : function(){
33351         
33352         var cfg = Roo.apply({}, Roo.bootstrap.LayoutMasonry.superclass.getAutoCreate.call(this));
33353         
33354         var cfg = {
33355             tag: this.tag,
33356             cls: 'blog-masonary-wrapper ' + this.cls,
33357             cn : {
33358                 cls : 'mas-boxes masonary'
33359             }
33360         };
33361         
33362         return cfg;
33363     },
33364     
33365     getChildContainer: function( )
33366     {
33367         if (this.boxesEl) {
33368             return this.boxesEl;
33369         }
33370         
33371         this.boxesEl = this.el.select('.mas-boxes').first();
33372         
33373         return this.boxesEl;
33374     },
33375     
33376     
33377     initEvents : function()
33378     {
33379         var _this = this;
33380         
33381         if(this.isAutoInitial){
33382             Roo.log('hook children rendered');
33383             this.on('childrenrendered', function() {
33384                 Roo.log('children rendered');
33385                 _this.initial();
33386             } ,this);
33387         }
33388     },
33389     
33390     initial : function()
33391     {
33392         this.selectedBrick = [];
33393         
33394         this.currentSize = this.el.getBox(true);
33395         
33396         Roo.EventManager.onWindowResize(this.resize, this); 
33397
33398         if(!this.isAutoInitial){
33399             this.layout();
33400             return;
33401         }
33402         
33403         this.layout();
33404         
33405         return;
33406         //this.layout.defer(500,this);
33407         
33408     },
33409     
33410     resize : function()
33411     {
33412         var cs = this.el.getBox(true);
33413         
33414         if (
33415                 this.currentSize.width == cs.width && 
33416                 this.currentSize.x == cs.x && 
33417                 this.currentSize.height == cs.height && 
33418                 this.currentSize.y == cs.y 
33419         ) {
33420             Roo.log("no change in with or X or Y");
33421             return;
33422         }
33423         
33424         this.currentSize = cs;
33425         
33426         this.layout();
33427         
33428     },
33429     
33430     layout : function()
33431     {   
33432         this._resetLayout();
33433         
33434         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
33435         
33436         this.layoutItems( isInstant );
33437       
33438         this._isLayoutInited = true;
33439         
33440         this.fireEvent('layout', this);
33441         
33442     },
33443     
33444     _resetLayout : function()
33445     {
33446         if(this.isHorizontal){
33447             this.horizontalMeasureColumns();
33448             return;
33449         }
33450         
33451         this.verticalMeasureColumns();
33452         
33453     },
33454     
33455     verticalMeasureColumns : function()
33456     {
33457         this.getContainerWidth();
33458         
33459 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33460 //            this.colWidth = Math.floor(this.containerWidth * 0.8);
33461 //            return;
33462 //        }
33463         
33464         var boxWidth = this.boxWidth + this.padWidth;
33465         
33466         if(this.containerWidth < this.boxWidth){
33467             boxWidth = this.containerWidth
33468         }
33469         
33470         var containerWidth = this.containerWidth;
33471         
33472         var cols = Math.floor(containerWidth / boxWidth);
33473         
33474         this.cols = Math.max( cols, 1 );
33475         
33476         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
33477         
33478         var totalBoxWidth = this.cols * boxWidth - this.padWidth;
33479         
33480         var avail = Math.floor((containerWidth - totalBoxWidth) / this.cols);
33481         
33482         this.colWidth = boxWidth + avail - this.padWidth;
33483         
33484         this.unitWidth = Math.round((this.colWidth - (this.gutter * 2)) / 3);
33485         this.unitHeight = this.boxHeight > 0 ? this.boxHeight  : this.unitWidth;
33486     },
33487     
33488     horizontalMeasureColumns : function()
33489     {
33490         this.getContainerWidth();
33491         
33492         var boxWidth = this.boxWidth;
33493         
33494         if(this.containerWidth < boxWidth){
33495             boxWidth = this.containerWidth;
33496         }
33497         
33498         this.unitWidth = Math.floor((boxWidth - (this.gutter * 2)) / 3);
33499         
33500         this.el.setHeight(boxWidth);
33501         
33502     },
33503     
33504     getContainerWidth : function()
33505     {
33506         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
33507     },
33508     
33509     layoutItems : function( isInstant )
33510     {
33511         Roo.log(this.bricks);
33512         
33513         var items = Roo.apply([], this.bricks);
33514         
33515         if(this.isHorizontal){
33516             this._horizontalLayoutItems( items , isInstant );
33517             return;
33518         }
33519         
33520 //        if(Roo.lib.Dom.getViewWidth() < 768 && this.isAlternative){
33521 //            this._verticalAlternativeLayoutItems( items , isInstant );
33522 //            return;
33523 //        }
33524         
33525         this._verticalLayoutItems( items , isInstant );
33526         
33527     },
33528     
33529     _verticalLayoutItems : function ( items , isInstant)
33530     {
33531         if ( !items || !items.length ) {
33532             return;
33533         }
33534         
33535         var standard = [
33536             ['xs', 'xs', 'xs', 'tall'],
33537             ['xs', 'xs', 'tall'],
33538             ['xs', 'xs', 'sm'],
33539             ['xs', 'xs', 'xs'],
33540             ['xs', 'tall'],
33541             ['xs', 'sm'],
33542             ['xs', 'xs'],
33543             ['xs'],
33544             
33545             ['sm', 'xs', 'xs'],
33546             ['sm', 'xs'],
33547             ['sm'],
33548             
33549             ['tall', 'xs', 'xs', 'xs'],
33550             ['tall', 'xs', 'xs'],
33551             ['tall', 'xs'],
33552             ['tall']
33553             
33554         ];
33555         
33556         var queue = [];
33557         
33558         var boxes = [];
33559         
33560         var box = [];
33561         
33562         Roo.each(items, function(item, k){
33563             
33564             switch (item.size) {
33565                 // these layouts take up a full box,
33566                 case 'md' :
33567                 case 'md-left' :
33568                 case 'md-right' :
33569                 case 'wide' :
33570                     
33571                     if(box.length){
33572                         boxes.push(box);
33573                         box = [];
33574                     }
33575                     
33576                     boxes.push([item]);
33577                     
33578                     break;
33579                     
33580                 case 'xs' :
33581                 case 'sm' :
33582                 case 'tall' :
33583                     
33584                     box.push(item);
33585                     
33586                     break;
33587                 default :
33588                     break;
33589                     
33590             }
33591             
33592         }, this);
33593         
33594         if(box.length){
33595             boxes.push(box);
33596             box = [];
33597         }
33598         
33599         var filterPattern = function(box, length)
33600         {
33601             if(!box.length){
33602                 return;
33603             }
33604             
33605             var match = false;
33606             
33607             var pattern = box.slice(0, length);
33608             
33609             var format = [];
33610             
33611             Roo.each(pattern, function(i){
33612                 format.push(i.size);
33613             }, this);
33614             
33615             Roo.each(standard, function(s){
33616                 
33617                 if(String(s) != String(format)){
33618                     return;
33619                 }
33620                 
33621                 match = true;
33622                 return false;
33623                 
33624             }, this);
33625             
33626             if(!match && length == 1){
33627                 return;
33628             }
33629             
33630             if(!match){
33631                 filterPattern(box, length - 1);
33632                 return;
33633             }
33634                 
33635             queue.push(pattern);
33636
33637             box = box.slice(length, box.length);
33638
33639             filterPattern(box, 4);
33640
33641             return;
33642             
33643         }
33644         
33645         Roo.each(boxes, function(box, k){
33646             
33647             if(!box.length){
33648                 return;
33649             }
33650             
33651             if(box.length == 1){
33652                 queue.push(box);
33653                 return;
33654             }
33655             
33656             filterPattern(box, 4);
33657             
33658         }, this);
33659         
33660         this._processVerticalLayoutQueue( queue, isInstant );
33661         
33662     },
33663     
33664 //    _verticalAlternativeLayoutItems : function( items , isInstant )
33665 //    {
33666 //        if ( !items || !items.length ) {
33667 //            return;
33668 //        }
33669 //
33670 //        this._processVerticalAlternativeLayoutQueue( items, isInstant );
33671 //        
33672 //    },
33673     
33674     _horizontalLayoutItems : function ( items , isInstant)
33675     {
33676         if ( !items || !items.length || items.length < 3) {
33677             return;
33678         }
33679         
33680         items.reverse();
33681         
33682         var eItems = items.slice(0, 3);
33683         
33684         items = items.slice(3, items.length);
33685         
33686         var standard = [
33687             ['xs', 'xs', 'xs', 'wide'],
33688             ['xs', 'xs', 'wide'],
33689             ['xs', 'xs', 'sm'],
33690             ['xs', 'xs', 'xs'],
33691             ['xs', 'wide'],
33692             ['xs', 'sm'],
33693             ['xs', 'xs'],
33694             ['xs'],
33695             
33696             ['sm', 'xs', 'xs'],
33697             ['sm', 'xs'],
33698             ['sm'],
33699             
33700             ['wide', 'xs', 'xs', 'xs'],
33701             ['wide', 'xs', 'xs'],
33702             ['wide', 'xs'],
33703             ['wide'],
33704             
33705             ['wide-thin']
33706         ];
33707         
33708         var queue = [];
33709         
33710         var boxes = [];
33711         
33712         var box = [];
33713         
33714         Roo.each(items, function(item, k){
33715             
33716             switch (item.size) {
33717                 case 'md' :
33718                 case 'md-left' :
33719                 case 'md-right' :
33720                 case 'tall' :
33721                     
33722                     if(box.length){
33723                         boxes.push(box);
33724                         box = [];
33725                     }
33726                     
33727                     boxes.push([item]);
33728                     
33729                     break;
33730                     
33731                 case 'xs' :
33732                 case 'sm' :
33733                 case 'wide' :
33734                 case 'wide-thin' :
33735                     
33736                     box.push(item);
33737                     
33738                     break;
33739                 default :
33740                     break;
33741                     
33742             }
33743             
33744         }, this);
33745         
33746         if(box.length){
33747             boxes.push(box);
33748             box = [];
33749         }
33750         
33751         var filterPattern = function(box, length)
33752         {
33753             if(!box.length){
33754                 return;
33755             }
33756             
33757             var match = false;
33758             
33759             var pattern = box.slice(0, length);
33760             
33761             var format = [];
33762             
33763             Roo.each(pattern, function(i){
33764                 format.push(i.size);
33765             }, this);
33766             
33767             Roo.each(standard, function(s){
33768                 
33769                 if(String(s) != String(format)){
33770                     return;
33771                 }
33772                 
33773                 match = true;
33774                 return false;
33775                 
33776             }, this);
33777             
33778             if(!match && length == 1){
33779                 return;
33780             }
33781             
33782             if(!match){
33783                 filterPattern(box, length - 1);
33784                 return;
33785             }
33786                 
33787             queue.push(pattern);
33788
33789             box = box.slice(length, box.length);
33790
33791             filterPattern(box, 4);
33792
33793             return;
33794             
33795         }
33796         
33797         Roo.each(boxes, function(box, k){
33798             
33799             if(!box.length){
33800                 return;
33801             }
33802             
33803             if(box.length == 1){
33804                 queue.push(box);
33805                 return;
33806             }
33807             
33808             filterPattern(box, 4);
33809             
33810         }, this);
33811         
33812         
33813         var prune = [];
33814         
33815         var pos = this.el.getBox(true);
33816         
33817         var minX = pos.x;
33818         
33819         var maxX = pos.right - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
33820         
33821         var hit_end = false;
33822         
33823         Roo.each(queue, function(box){
33824             
33825             if(hit_end){
33826                 
33827                 Roo.each(box, function(b){
33828                 
33829                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33830                     b.el.hide();
33831
33832                 }, this);
33833
33834                 return;
33835             }
33836             
33837             var mx = 0;
33838             
33839             Roo.each(box, function(b){
33840                 
33841                 b.el.setVisibilityMode(Roo.Element.DISPLAY);
33842                 b.el.show();
33843
33844                 mx = Math.max(mx, b.x);
33845                 
33846             }, this);
33847             
33848             maxX = maxX - this.unitWidth * mx - this.gutter * (mx - 1) - this.padWidth;
33849             
33850             if(maxX < minX){
33851                 
33852                 Roo.each(box, function(b){
33853                 
33854                     b.el.setVisibilityMode(Roo.Element.DISPLAY);
33855                     b.el.hide();
33856                     
33857                 }, this);
33858                 
33859                 hit_end = true;
33860                 
33861                 return;
33862             }
33863             
33864             prune.push(box);
33865             
33866         }, this);
33867         
33868         this._processHorizontalLayoutQueue( prune, eItems, isInstant );
33869     },
33870     
33871     /** Sets position of item in DOM
33872     * @param {Element} item
33873     * @param {Number} x - horizontal position
33874     * @param {Number} y - vertical position
33875     * @param {Boolean} isInstant - disables transitions
33876     */
33877     _processVerticalLayoutQueue : function( queue, isInstant )
33878     {
33879         var pos = this.el.getBox(true);
33880         var x = pos.x;
33881         var y = pos.y;
33882         var maxY = [];
33883         
33884         for (var i = 0; i < this.cols; i++){
33885             maxY[i] = pos.y;
33886         }
33887         
33888         Roo.each(queue, function(box, k){
33889             
33890             var col = k % this.cols;
33891             
33892             Roo.each(box, function(b,kk){
33893                 
33894                 b.el.position('absolute');
33895                 
33896                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
33897                 var height = Math.floor(this.unitHeight * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
33898                 
33899                 if(b.size == 'md-left' || b.size == 'md-right'){
33900                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
33901                     height = Math.floor(this.unitHeight * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
33902                 }
33903                 
33904                 b.el.setWidth(width);
33905                 b.el.setHeight(height);
33906                 // iframe?
33907                 b.el.select('iframe',true).setSize(width,height);
33908                 
33909             }, this);
33910             
33911             for (var i = 0; i < this.cols; i++){
33912                 
33913                 if(maxY[i] < maxY[col]){
33914                     col = i;
33915                     continue;
33916                 }
33917                 
33918                 col = Math.min(col, i);
33919                 
33920             }
33921             
33922             x = pos.x + col * (this.colWidth + this.padWidth);
33923             
33924             y = maxY[col];
33925             
33926             var positions = [];
33927             
33928             switch (box.length){
33929                 case 1 :
33930                     positions = this.getVerticalOneBoxColPositions(x, y, box);
33931                     break;
33932                 case 2 :
33933                     positions = this.getVerticalTwoBoxColPositions(x, y, box);
33934                     break;
33935                 case 3 :
33936                     positions = this.getVerticalThreeBoxColPositions(x, y, box);
33937                     break;
33938                 case 4 :
33939                     positions = this.getVerticalFourBoxColPositions(x, y, box);
33940                     break;
33941                 default :
33942                     break;
33943             }
33944             
33945             Roo.each(box, function(b,kk){
33946                 
33947                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
33948                 
33949                 var sz = b.el.getSize();
33950                 
33951                 maxY[col] = Math.max(maxY[col], positions[kk].y + sz.height + this.padWidth);
33952                 
33953             }, this);
33954             
33955         }, this);
33956         
33957         var mY = 0;
33958         
33959         for (var i = 0; i < this.cols; i++){
33960             mY = Math.max(mY, maxY[i]);
33961         }
33962         
33963         this.el.setHeight(mY - pos.y);
33964         
33965     },
33966     
33967 //    _processVerticalAlternativeLayoutQueue : function( items, isInstant )
33968 //    {
33969 //        var pos = this.el.getBox(true);
33970 //        var x = pos.x;
33971 //        var y = pos.y;
33972 //        var maxX = pos.right;
33973 //        
33974 //        var maxHeight = 0;
33975 //        
33976 //        Roo.each(items, function(item, k){
33977 //            
33978 //            var c = k % 2;
33979 //            
33980 //            item.el.position('absolute');
33981 //                
33982 //            var width = Math.floor(this.colWidth + item.el.getPadding('lr'));
33983 //
33984 //            item.el.setWidth(width);
33985 //
33986 //            var height = Math.floor(this.colWidth * item.y / item.x + item.el.getPadding('tb'));
33987 //
33988 //            item.el.setHeight(height);
33989 //            
33990 //            if(c == 0){
33991 //                item.el.setXY([x, y], isInstant ? false : true);
33992 //            } else {
33993 //                item.el.setXY([maxX - width, y], isInstant ? false : true);
33994 //            }
33995 //            
33996 //            y = y + height + this.alternativePadWidth;
33997 //            
33998 //            maxHeight = maxHeight + height + this.alternativePadWidth;
33999 //            
34000 //        }, this);
34001 //        
34002 //        this.el.setHeight(maxHeight);
34003 //        
34004 //    },
34005     
34006     _processHorizontalLayoutQueue : function( queue, eItems, isInstant )
34007     {
34008         var pos = this.el.getBox(true);
34009         
34010         var minX = pos.x;
34011         var minY = pos.y;
34012         
34013         var maxX = pos.right;
34014         
34015         this._processHorizontalEndItem(eItems, maxX, minX, minY, isInstant);
34016         
34017         var maxX = maxX - this.unitWidth * 3 - this.gutter * 2 - this.padWidth;
34018         
34019         Roo.each(queue, function(box, k){
34020             
34021             Roo.each(box, function(b, kk){
34022                 
34023                 b.el.position('absolute');
34024                 
34025                 var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34026                 var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34027                 
34028                 if(b.size == 'md-left' || b.size == 'md-right'){
34029                     width = Math.floor(this.unitWidth * (b.x - 1) + (this.gutter * (b.x - 2)) + b.el.getPadding('lr'));
34030                     height = Math.floor(this.unitWidth * (b.y - 1) + (this.gutter * (b.y - 2)) + b.el.getPadding('tb'));
34031                 }
34032                 
34033                 b.el.setWidth(width);
34034                 b.el.setHeight(height);
34035                 
34036             }, this);
34037             
34038             if(!box.length){
34039                 return;
34040             }
34041             
34042             var positions = [];
34043             
34044             switch (box.length){
34045                 case 1 :
34046                     positions = this.getHorizontalOneBoxColPositions(maxX, minY, box);
34047                     break;
34048                 case 2 :
34049                     positions = this.getHorizontalTwoBoxColPositions(maxX, minY, box);
34050                     break;
34051                 case 3 :
34052                     positions = this.getHorizontalThreeBoxColPositions(maxX, minY, box);
34053                     break;
34054                 case 4 :
34055                     positions = this.getHorizontalFourBoxColPositions(maxX, minY, box);
34056                     break;
34057                 default :
34058                     break;
34059             }
34060             
34061             Roo.each(box, function(b,kk){
34062                 
34063                 b.el.setXY([positions[kk].x, positions[kk].y], isInstant ? false : true);
34064                 
34065                 maxX = Math.min(maxX, positions[kk].x - this.padWidth);
34066                 
34067             }, this);
34068             
34069         }, this);
34070         
34071     },
34072     
34073     _processHorizontalEndItem : function(eItems, maxX, minX, minY, isInstant)
34074     {
34075         Roo.each(eItems, function(b,k){
34076             
34077             b.size = (k == 0) ? 'sm' : 'xs';
34078             b.x = (k == 0) ? 2 : 1;
34079             b.y = (k == 0) ? 2 : 1;
34080             
34081             b.el.position('absolute');
34082             
34083             var width = Math.floor(this.unitWidth * b.x + (this.gutter * (b.x - 1)) + b.el.getPadding('lr'));
34084                 
34085             b.el.setWidth(width);
34086             
34087             var height = Math.floor(this.unitWidth * b.y + (this.gutter * (b.y - 1)) + b.el.getPadding('tb'));
34088             
34089             b.el.setHeight(height);
34090             
34091         }, this);
34092
34093         var positions = [];
34094         
34095         positions.push({
34096             x : maxX - this.unitWidth * 2 - this.gutter,
34097             y : minY
34098         });
34099         
34100         positions.push({
34101             x : maxX - this.unitWidth,
34102             y : minY + (this.unitWidth + this.gutter) * 2
34103         });
34104         
34105         positions.push({
34106             x : maxX - this.unitWidth * 3 - this.gutter * 2,
34107             y : minY
34108         });
34109         
34110         Roo.each(eItems, function(b,k){
34111             
34112             b.el.setXY([positions[k].x, positions[k].y], isInstant ? false : true);
34113
34114         }, this);
34115         
34116     },
34117     
34118     getVerticalOneBoxColPositions : function(x, y, box)
34119     {
34120         var pos = [];
34121         
34122         var rand = Math.floor(Math.random() * ((4 - box[0].x)));
34123         
34124         if(box[0].size == 'md-left'){
34125             rand = 0;
34126         }
34127         
34128         if(box[0].size == 'md-right'){
34129             rand = 1;
34130         }
34131         
34132         pos.push({
34133             x : x + (this.unitWidth + this.gutter) * rand,
34134             y : y
34135         });
34136         
34137         return pos;
34138     },
34139     
34140     getVerticalTwoBoxColPositions : function(x, y, box)
34141     {
34142         var pos = [];
34143         
34144         if(box[0].size == 'xs'){
34145             
34146             pos.push({
34147                 x : x,
34148                 y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[1].y))
34149             });
34150
34151             pos.push({
34152                 x : x + (this.unitWidth + this.gutter) * (3 - box[1].x),
34153                 y : y
34154             });
34155             
34156             return pos;
34157             
34158         }
34159         
34160         pos.push({
34161             x : x,
34162             y : y
34163         });
34164
34165         pos.push({
34166             x : x + (this.unitWidth + this.gutter) * 2,
34167             y : y + ((this.unitHeight + this.gutter) * Math.floor(Math.random() * box[0].y))
34168         });
34169         
34170         return pos;
34171         
34172     },
34173     
34174     getVerticalThreeBoxColPositions : function(x, y, box)
34175     {
34176         var pos = [];
34177         
34178         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34179             
34180             pos.push({
34181                 x : x,
34182                 y : y
34183             });
34184
34185             pos.push({
34186                 x : x + (this.unitWidth + this.gutter) * 1,
34187                 y : y
34188             });
34189             
34190             pos.push({
34191                 x : x + (this.unitWidth + this.gutter) * 2,
34192                 y : y
34193             });
34194             
34195             return pos;
34196             
34197         }
34198         
34199         if(box[0].size == 'xs' && box[1].size == 'xs'){
34200             
34201             pos.push({
34202                 x : x,
34203                 y : y
34204             });
34205
34206             pos.push({
34207                 x : x,
34208                 y : y + ((this.unitHeight + this.gutter) * (box[2].y - 1))
34209             });
34210             
34211             pos.push({
34212                 x : x + (this.unitWidth + this.gutter) * 1,
34213                 y : y
34214             });
34215             
34216             return pos;
34217             
34218         }
34219         
34220         pos.push({
34221             x : x,
34222             y : y
34223         });
34224
34225         pos.push({
34226             x : x + (this.unitWidth + this.gutter) * 2,
34227             y : y
34228         });
34229
34230         pos.push({
34231             x : x + (this.unitWidth + this.gutter) * 2,
34232             y : y + (this.unitHeight + this.gutter) * (box[0].y - 1)
34233         });
34234             
34235         return pos;
34236         
34237     },
34238     
34239     getVerticalFourBoxColPositions : function(x, y, box)
34240     {
34241         var pos = [];
34242         
34243         if(box[0].size == 'xs'){
34244             
34245             pos.push({
34246                 x : x,
34247                 y : y
34248             });
34249
34250             pos.push({
34251                 x : x,
34252                 y : y + (this.unitHeight + this.gutter) * 1
34253             });
34254             
34255             pos.push({
34256                 x : x,
34257                 y : y + (this.unitHeight + this.gutter) * 2
34258             });
34259             
34260             pos.push({
34261                 x : x + (this.unitWidth + this.gutter) * 1,
34262                 y : y
34263             });
34264             
34265             return pos;
34266             
34267         }
34268         
34269         pos.push({
34270             x : x,
34271             y : y
34272         });
34273
34274         pos.push({
34275             x : x + (this.unitWidth + this.gutter) * 2,
34276             y : y
34277         });
34278
34279         pos.push({
34280             x : x + (this.unitHeightunitWidth + this.gutter) * 2,
34281             y : y + (this.unitHeight + this.gutter) * 1
34282         });
34283
34284         pos.push({
34285             x : x + (this.unitWidth + this.gutter) * 2,
34286             y : y + (this.unitWidth + this.gutter) * 2
34287         });
34288
34289         return pos;
34290         
34291     },
34292     
34293     getHorizontalOneBoxColPositions : function(maxX, minY, box)
34294     {
34295         var pos = [];
34296         
34297         if(box[0].size == 'md-left'){
34298             pos.push({
34299                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34300                 y : minY
34301             });
34302             
34303             return pos;
34304         }
34305         
34306         if(box[0].size == 'md-right'){
34307             pos.push({
34308                 x : maxX - this.unitWidth * (box[0].x - 1) - this.gutter * (box[0].x - 2),
34309                 y : minY + (this.unitWidth + this.gutter) * 1
34310             });
34311             
34312             return pos;
34313         }
34314         
34315         var rand = Math.floor(Math.random() * (4 - box[0].y));
34316         
34317         pos.push({
34318             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34319             y : minY + (this.unitWidth + this.gutter) * rand
34320         });
34321         
34322         return pos;
34323         
34324     },
34325     
34326     getHorizontalTwoBoxColPositions : function(maxX, minY, box)
34327     {
34328         var pos = [];
34329         
34330         if(box[0].size == 'xs'){
34331             
34332             pos.push({
34333                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34334                 y : minY
34335             });
34336
34337             pos.push({
34338                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34339                 y : minY + (this.unitWidth + this.gutter) * (3 - box[1].y)
34340             });
34341             
34342             return pos;
34343             
34344         }
34345         
34346         pos.push({
34347             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34348             y : minY
34349         });
34350
34351         pos.push({
34352             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34353             y : minY + (this.unitWidth + this.gutter) * 2
34354         });
34355         
34356         return pos;
34357         
34358     },
34359     
34360     getHorizontalThreeBoxColPositions : function(maxX, minY, box)
34361     {
34362         var pos = [];
34363         
34364         if(box[0].size == 'xs' && box[1].size == 'xs' && box[2].size == 'xs'){
34365             
34366             pos.push({
34367                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34368                 y : minY
34369             });
34370
34371             pos.push({
34372                 x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34373                 y : minY + (this.unitWidth + this.gutter) * 1
34374             });
34375             
34376             pos.push({
34377                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34378                 y : minY + (this.unitWidth + this.gutter) * 2
34379             });
34380             
34381             return pos;
34382             
34383         }
34384         
34385         if(box[0].size == 'xs' && box[1].size == 'xs'){
34386             
34387             pos.push({
34388                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34389                 y : minY
34390             });
34391
34392             pos.push({
34393                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34394                 y : minY
34395             });
34396             
34397             pos.push({
34398                 x : maxX - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34399                 y : minY + (this.unitWidth + this.gutter) * 1
34400             });
34401             
34402             return pos;
34403             
34404         }
34405         
34406         pos.push({
34407             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34408             y : minY
34409         });
34410
34411         pos.push({
34412             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34413             y : minY + (this.unitWidth + this.gutter) * 2
34414         });
34415
34416         pos.push({
34417             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34418             y : minY + (this.unitWidth + this.gutter) * 2
34419         });
34420             
34421         return pos;
34422         
34423     },
34424     
34425     getHorizontalFourBoxColPositions : function(maxX, minY, box)
34426     {
34427         var pos = [];
34428         
34429         if(box[0].size == 'xs'){
34430             
34431             pos.push({
34432                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34433                 y : minY
34434             });
34435
34436             pos.push({
34437                 x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1) - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34438                 y : minY
34439             });
34440             
34441             pos.push({
34442                 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),
34443                 y : minY
34444             });
34445             
34446             pos.push({
34447                 x : maxX - this.unitWidth * box[3].x - this.gutter * (box[3].x - 1),
34448                 y : minY + (this.unitWidth + this.gutter) * 1
34449             });
34450             
34451             return pos;
34452             
34453         }
34454         
34455         pos.push({
34456             x : maxX - this.unitWidth * box[0].x - this.gutter * (box[0].x - 1),
34457             y : minY
34458         });
34459         
34460         pos.push({
34461             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1),
34462             y : minY + (this.unitWidth + this.gutter) * 2
34463         });
34464         
34465         pos.push({
34466             x : maxX - this.unitWidth * box[1].x - this.gutter * (box[1].x - 1) - this.unitWidth * box[2].x - this.gutter * (box[2].x - 1),
34467             y : minY + (this.unitWidth + this.gutter) * 2
34468         });
34469         
34470         pos.push({
34471             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),
34472             y : minY + (this.unitWidth + this.gutter) * 2
34473         });
34474
34475         return pos;
34476         
34477     },
34478     
34479     /**
34480     * remove a Masonry Brick
34481     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to remove
34482     */
34483     removeBrick : function(brick_id)
34484     {
34485         if (!brick_id) {
34486             return;
34487         }
34488         
34489         for (var i = 0; i<this.bricks.length; i++) {
34490             if (this.bricks[i].id == brick_id) {
34491                 this.bricks.splice(i,1);
34492                 this.el.dom.removeChild(Roo.get(brick_id).dom);
34493                 this.initial();
34494             }
34495         }
34496     },
34497     
34498     /**
34499     * adds a Masonry Brick
34500     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34501     */
34502     addBrick : function(cfg)
34503     {
34504         var cn = new Roo.bootstrap.MasonryBrick(cfg);
34505         //this.register(cn);
34506         cn.parentId = this.id;
34507         cn.render(this.el);
34508         return cn;
34509     },
34510     
34511     /**
34512     * register a Masonry Brick
34513     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
34514     */
34515     
34516     register : function(brick)
34517     {
34518         this.bricks.push(brick);
34519         brick.masonryId = this.id;
34520     },
34521     
34522     /**
34523     * clear all the Masonry Brick
34524     */
34525     clearAll : function()
34526     {
34527         this.bricks = [];
34528         //this.getChildContainer().dom.innerHTML = "";
34529         this.el.dom.innerHTML = '';
34530     },
34531     
34532     getSelected : function()
34533     {
34534         if (!this.selectedBrick) {
34535             return false;
34536         }
34537         
34538         return this.selectedBrick;
34539     }
34540 });
34541
34542 Roo.apply(Roo.bootstrap.LayoutMasonry, {
34543     
34544     groups: {},
34545      /**
34546     * register a Masonry Layout
34547     * @param {Roo.bootstrap.LayoutMasonry} the masonry layout to add
34548     */
34549     
34550     register : function(layout)
34551     {
34552         this.groups[layout.id] = layout;
34553     },
34554     /**
34555     * fetch a  Masonry Layout based on the masonry layout ID
34556     * @param {string} the masonry layout to add
34557     * @returns {Roo.bootstrap.LayoutMasonry} the masonry layout
34558     */
34559     
34560     get: function(layout_id) {
34561         if (typeof(this.groups[layout_id]) == 'undefined') {
34562             return false;
34563         }
34564         return this.groups[layout_id] ;
34565     }
34566     
34567     
34568     
34569 });
34570
34571  
34572
34573  /**
34574  *
34575  * This is based on 
34576  * http://masonry.desandro.com
34577  *
34578  * The idea is to render all the bricks based on vertical width...
34579  *
34580  * The original code extends 'outlayer' - we might need to use that....
34581  * 
34582  */
34583
34584
34585 /**
34586  * @class Roo.bootstrap.LayoutMasonryAuto
34587  * @extends Roo.bootstrap.Component
34588  * Bootstrap Layout Masonry class
34589  * 
34590  * @constructor
34591  * Create a new Element
34592  * @param {Object} config The config object
34593  */
34594
34595 Roo.bootstrap.LayoutMasonryAuto = function(config){
34596     Roo.bootstrap.LayoutMasonryAuto.superclass.constructor.call(this, config);
34597 };
34598
34599 Roo.extend(Roo.bootstrap.LayoutMasonryAuto, Roo.bootstrap.Component,  {
34600     
34601       /**
34602      * @cfg {Boolean} isFitWidth  - resize the width..
34603      */   
34604     isFitWidth : false,  // options..
34605     /**
34606      * @cfg {Boolean} isOriginLeft = left align?
34607      */   
34608     isOriginLeft : true,
34609     /**
34610      * @cfg {Boolean} isOriginTop = top align?
34611      */   
34612     isOriginTop : false,
34613     /**
34614      * @cfg {Boolean} isLayoutInstant = no animation?
34615      */   
34616     isLayoutInstant : false, // needed?
34617     /**
34618      * @cfg {Boolean} isResizingContainer = not sure if this is used..
34619      */   
34620     isResizingContainer : true,
34621     /**
34622      * @cfg {Number} columnWidth  width of the columns 
34623      */   
34624     
34625     columnWidth : 0,
34626     
34627     /**
34628      * @cfg {Number} maxCols maximum number of columns
34629      */   
34630     
34631     maxCols: 0,
34632     /**
34633      * @cfg {Number} padHeight padding below box..
34634      */   
34635     
34636     padHeight : 10, 
34637     
34638     /**
34639      * @cfg {Boolean} isAutoInitial defalut true
34640      */   
34641     
34642     isAutoInitial : true, 
34643     
34644     // private?
34645     gutter : 0,
34646     
34647     containerWidth: 0,
34648     initialColumnWidth : 0,
34649     currentSize : null,
34650     
34651     colYs : null, // array.
34652     maxY : 0,
34653     padWidth: 10,
34654     
34655     
34656     tag: 'div',
34657     cls: '',
34658     bricks: null, //CompositeElement
34659     cols : 0, // array?
34660     // element : null, // wrapped now this.el
34661     _isLayoutInited : null, 
34662     
34663     
34664     getAutoCreate : function(){
34665         
34666         var cfg = {
34667             tag: this.tag,
34668             cls: 'blog-masonary-wrapper ' + this.cls,
34669             cn : {
34670                 cls : 'mas-boxes masonary'
34671             }
34672         };
34673         
34674         return cfg;
34675     },
34676     
34677     getChildContainer: function( )
34678     {
34679         if (this.boxesEl) {
34680             return this.boxesEl;
34681         }
34682         
34683         this.boxesEl = this.el.select('.mas-boxes').first();
34684         
34685         return this.boxesEl;
34686     },
34687     
34688     
34689     initEvents : function()
34690     {
34691         var _this = this;
34692         
34693         if(this.isAutoInitial){
34694             Roo.log('hook children rendered');
34695             this.on('childrenrendered', function() {
34696                 Roo.log('children rendered');
34697                 _this.initial();
34698             } ,this);
34699         }
34700         
34701     },
34702     
34703     initial : function()
34704     {
34705         this.reloadItems();
34706
34707         this.currentSize = this.el.getBox(true);
34708
34709         /// was window resize... - let's see if this works..
34710         Roo.EventManager.onWindowResize(this.resize, this); 
34711
34712         if(!this.isAutoInitial){
34713             this.layout();
34714             return;
34715         }
34716         
34717         this.layout.defer(500,this);
34718     },
34719     
34720     reloadItems: function()
34721     {
34722         this.bricks = this.el.select('.masonry-brick', true);
34723         
34724         this.bricks.each(function(b) {
34725             //Roo.log(b.getSize());
34726             if (!b.attr('originalwidth')) {
34727                 b.attr('originalwidth',  b.getSize().width);
34728             }
34729             
34730         });
34731         
34732         Roo.log(this.bricks.elements.length);
34733     },
34734     
34735     resize : function()
34736     {
34737         Roo.log('resize');
34738         var cs = this.el.getBox(true);
34739         
34740         if (this.currentSize.width == cs.width && this.currentSize.x == cs.x ) {
34741             Roo.log("no change in with or X");
34742             return;
34743         }
34744         this.currentSize = cs;
34745         this.layout();
34746     },
34747     
34748     layout : function()
34749     {
34750          Roo.log('layout');
34751         this._resetLayout();
34752         //this._manageStamps();
34753       
34754         // don't animate first layout
34755         var isInstant = this.isLayoutInstant !== undefined ? this.isLayoutInstant : !this._isLayoutInited;
34756         this.layoutItems( isInstant );
34757       
34758         // flag for initalized
34759         this._isLayoutInited = true;
34760     },
34761     
34762     layoutItems : function( isInstant )
34763     {
34764         //var items = this._getItemsForLayout( this.items );
34765         // original code supports filtering layout items.. we just ignore it..
34766         
34767         this._layoutItems( this.bricks , isInstant );
34768       
34769         this._postLayout();
34770     },
34771     _layoutItems : function ( items , isInstant)
34772     {
34773        //this.fireEvent( 'layout', this, items );
34774     
34775
34776         if ( !items || !items.elements.length ) {
34777           // no items, emit event with empty array
34778             return;
34779         }
34780
34781         var queue = [];
34782         items.each(function(item) {
34783             Roo.log("layout item");
34784             Roo.log(item);
34785             // get x/y object from method
34786             var position = this._getItemLayoutPosition( item );
34787             // enqueue
34788             position.item = item;
34789             position.isInstant = isInstant; // || item.isLayoutInstant; << not set yet...
34790             queue.push( position );
34791         }, this);
34792       
34793         this._processLayoutQueue( queue );
34794     },
34795     /** Sets position of item in DOM
34796     * @param {Element} item
34797     * @param {Number} x - horizontal position
34798     * @param {Number} y - vertical position
34799     * @param {Boolean} isInstant - disables transitions
34800     */
34801     _processLayoutQueue : function( queue )
34802     {
34803         for ( var i=0, len = queue.length; i < len; i++ ) {
34804             var obj = queue[i];
34805             obj.item.position('absolute');
34806             obj.item.setXY([obj.x,obj.y], obj.isInstant ? false : true);
34807         }
34808     },
34809       
34810     
34811     /**
34812     * Any logic you want to do after each layout,
34813     * i.e. size the container
34814     */
34815     _postLayout : function()
34816     {
34817         this.resizeContainer();
34818     },
34819     
34820     resizeContainer : function()
34821     {
34822         if ( !this.isResizingContainer ) {
34823             return;
34824         }
34825         var size = this._getContainerSize();
34826         if ( size ) {
34827             this.el.setSize(size.width,size.height);
34828             this.boxesEl.setSize(size.width,size.height);
34829         }
34830     },
34831     
34832     
34833     
34834     _resetLayout : function()
34835     {
34836         //this.getSize();  // -- does not really do anything.. it probably applies left/right etc. to obuject but not used
34837         this.colWidth = this.el.getWidth();
34838         //this.gutter = this.el.getWidth(); 
34839         
34840         this.measureColumns();
34841
34842         // reset column Y
34843         var i = this.cols;
34844         this.colYs = [];
34845         while (i--) {
34846             this.colYs.push( 0 );
34847         }
34848     
34849         this.maxY = 0;
34850     },
34851
34852     measureColumns : function()
34853     {
34854         this.getContainerWidth();
34855       // if columnWidth is 0, default to outerWidth of first item
34856         if ( !this.columnWidth ) {
34857             var firstItem = this.bricks.first();
34858             Roo.log(firstItem);
34859             this.columnWidth  = this.containerWidth;
34860             if (firstItem && firstItem.attr('originalwidth') ) {
34861                 this.columnWidth = 1* (firstItem.attr('originalwidth') || firstItem.getWidth());
34862             }
34863             // columnWidth fall back to item of first element
34864             Roo.log("set column width?");
34865                         this.initialColumnWidth = this.columnWidth  ;
34866
34867             // if first elem has no width, default to size of container
34868             
34869         }
34870         
34871         
34872         if (this.initialColumnWidth) {
34873             this.columnWidth = this.initialColumnWidth;
34874         }
34875         
34876         
34877             
34878         // column width is fixed at the top - however if container width get's smaller we should
34879         // reduce it...
34880         
34881         // this bit calcs how man columns..
34882             
34883         var columnWidth = this.columnWidth += this.gutter;
34884       
34885         // calculate columns
34886         var containerWidth = this.containerWidth + this.gutter;
34887         
34888         var cols = (containerWidth - this.padWidth) / (columnWidth - this.padWidth);
34889         // fix rounding errors, typically with gutters
34890         var excess = columnWidth - containerWidth % columnWidth;
34891         
34892         
34893         // if overshoot is less than a pixel, round up, otherwise floor it
34894         var mathMethod = excess && excess < 1 ? 'round' : 'floor';
34895         cols = Math[ mathMethod ]( cols );
34896         this.cols = Math.max( cols, 1 );
34897         this.cols = this.maxCols > 0 ? Math.min( this.cols, this.maxCols ) : this.cols;
34898         
34899          // padding positioning..
34900         var totalColWidth = this.cols * this.columnWidth;
34901         var padavail = this.containerWidth - totalColWidth;
34902         // so for 2 columns - we need 3 'pads'
34903         
34904         var padNeeded = (1+this.cols) * this.padWidth;
34905         
34906         var padExtra = Math.floor((padavail - padNeeded) / this.cols);
34907         
34908         this.columnWidth += padExtra
34909         //this.padWidth = Math.floor(padavail /  ( this.cols));
34910         
34911         // adjust colum width so that padding is fixed??
34912         
34913         // we have 3 columns ... total = width * 3
34914         // we have X left over... that should be used by 
34915         
34916         //if (this.expandC) {
34917             
34918         //}
34919         
34920         
34921         
34922     },
34923     
34924     getContainerWidth : function()
34925     {
34926        /* // container is parent if fit width
34927         var container = this.isFitWidth ? this.element.parentNode : this.element;
34928         // check that this.size and size are there
34929         // IE8 triggers resize on body size change, so they might not be
34930         
34931         var size = getSize( container );  //FIXME
34932         this.containerWidth = size && size.innerWidth; //FIXME
34933         */
34934          
34935         this.containerWidth = this.el.getBox(true).width;  //maybe use getComputedWidth
34936         
34937     },
34938     
34939     _getItemLayoutPosition : function( item )  // what is item?
34940     {
34941         // we resize the item to our columnWidth..
34942       
34943         item.setWidth(this.columnWidth);
34944         item.autoBoxAdjust  = false;
34945         
34946         var sz = item.getSize();
34947  
34948         // how many columns does this brick span
34949         var remainder = this.containerWidth % this.columnWidth;
34950         
34951         var mathMethod = remainder && remainder < 1 ? 'round' : 'ceil';
34952         // round if off by 1 pixel, otherwise use ceil
34953         var colSpan = Math[ mathMethod ]( sz.width  / this.columnWidth );
34954         colSpan = Math.min( colSpan, this.cols );
34955         
34956         // normally this should be '1' as we dont' currently allow multi width columns..
34957         
34958         var colGroup = this._getColGroup( colSpan );
34959         // get the minimum Y value from the columns
34960         var minimumY = Math.min.apply( Math, colGroup );
34961         Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34962         
34963         var shortColIndex = colGroup.indexOf(  minimumY ); // broken on ie8..?? probably...
34964          
34965         // position the brick
34966         var position = {
34967             x: this.currentSize.x + (this.padWidth /2) + ((this.columnWidth + this.padWidth )* shortColIndex),
34968             y: this.currentSize.y + minimumY + this.padHeight
34969         };
34970         
34971         Roo.log(position);
34972         // apply setHeight to necessary columns
34973         var setHeight = minimumY + sz.height + this.padHeight;
34974         //Roo.log([ 'setHeight',  minimumY, sz.height, setHeight ]);
34975         
34976         var setSpan = this.cols + 1 - colGroup.length;
34977         for ( var i = 0; i < setSpan; i++ ) {
34978           this.colYs[ shortColIndex + i ] = setHeight ;
34979         }
34980       
34981         return position;
34982     },
34983     
34984     /**
34985      * @param {Number} colSpan - number of columns the element spans
34986      * @returns {Array} colGroup
34987      */
34988     _getColGroup : function( colSpan )
34989     {
34990         if ( colSpan < 2 ) {
34991           // if brick spans only one column, use all the column Ys
34992           return this.colYs;
34993         }
34994       
34995         var colGroup = [];
34996         // how many different places could this brick fit horizontally
34997         var groupCount = this.cols + 1 - colSpan;
34998         // for each group potential horizontal position
34999         for ( var i = 0; i < groupCount; i++ ) {
35000           // make an array of colY values for that one group
35001           var groupColYs = this.colYs.slice( i, i + colSpan );
35002           // and get the max value of the array
35003           colGroup[i] = Math.max.apply( Math, groupColYs );
35004         }
35005         return colGroup;
35006     },
35007     /*
35008     _manageStamp : function( stamp )
35009     {
35010         var stampSize =  stamp.getSize();
35011         var offset = stamp.getBox();
35012         // get the columns that this stamp affects
35013         var firstX = this.isOriginLeft ? offset.x : offset.right;
35014         var lastX = firstX + stampSize.width;
35015         var firstCol = Math.floor( firstX / this.columnWidth );
35016         firstCol = Math.max( 0, firstCol );
35017         
35018         var lastCol = Math.floor( lastX / this.columnWidth );
35019         // lastCol should not go over if multiple of columnWidth #425
35020         lastCol -= lastX % this.columnWidth ? 0 : 1;
35021         lastCol = Math.min( this.cols - 1, lastCol );
35022         
35023         // set colYs to bottom of the stamp
35024         var stampMaxY = ( this.isOriginTop ? offset.y : offset.bottom ) +
35025             stampSize.height;
35026             
35027         for ( var i = firstCol; i <= lastCol; i++ ) {
35028           this.colYs[i] = Math.max( stampMaxY, this.colYs[i] );
35029         }
35030     },
35031     */
35032     
35033     _getContainerSize : function()
35034     {
35035         this.maxY = Math.max.apply( Math, this.colYs );
35036         var size = {
35037             height: this.maxY
35038         };
35039       
35040         if ( this.isFitWidth ) {
35041             size.width = this._getContainerFitWidth();
35042         }
35043       
35044         return size;
35045     },
35046     
35047     _getContainerFitWidth : function()
35048     {
35049         var unusedCols = 0;
35050         // count unused columns
35051         var i = this.cols;
35052         while ( --i ) {
35053           if ( this.colYs[i] !== 0 ) {
35054             break;
35055           }
35056           unusedCols++;
35057         }
35058         // fit container to columns that have been used
35059         return ( this.cols - unusedCols ) * this.columnWidth - this.gutter;
35060     },
35061     
35062     needsResizeLayout : function()
35063     {
35064         var previousWidth = this.containerWidth;
35065         this.getContainerWidth();
35066         return previousWidth !== this.containerWidth;
35067     }
35068  
35069 });
35070
35071  
35072
35073  /*
35074  * - LGPL
35075  *
35076  * element
35077  * 
35078  */
35079
35080 /**
35081  * @class Roo.bootstrap.MasonryBrick
35082  * @extends Roo.bootstrap.Component
35083  * Bootstrap MasonryBrick class
35084  * 
35085  * @constructor
35086  * Create a new MasonryBrick
35087  * @param {Object} config The config object
35088  */
35089
35090 Roo.bootstrap.MasonryBrick = function(config){
35091     
35092     Roo.bootstrap.MasonryBrick.superclass.constructor.call(this, config);
35093     
35094     Roo.bootstrap.MasonryBrick.register(this);
35095     
35096     this.addEvents({
35097         // raw events
35098         /**
35099          * @event click
35100          * When a MasonryBrick is clcik
35101          * @param {Roo.bootstrap.MasonryBrick} this
35102          * @param {Roo.EventObject} e
35103          */
35104         "click" : true
35105     });
35106 };
35107
35108 Roo.extend(Roo.bootstrap.MasonryBrick, Roo.bootstrap.Component,  {
35109     
35110     /**
35111      * @cfg {String} title
35112      */   
35113     title : '',
35114     /**
35115      * @cfg {String} html
35116      */   
35117     html : '',
35118     /**
35119      * @cfg {String} bgimage
35120      */   
35121     bgimage : '',
35122     /**
35123      * @cfg {String} videourl
35124      */   
35125     videourl : '',
35126     /**
35127      * @cfg {String} cls
35128      */   
35129     cls : '',
35130     /**
35131      * @cfg {String} href
35132      */   
35133     href : '',
35134     /**
35135      * @cfg {String} size (xs|sm|md|md-left|md-right|tall|wide)
35136      */   
35137     size : 'xs',
35138     
35139     /**
35140      * @cfg {String} placetitle (center|bottom)
35141      */   
35142     placetitle : '',
35143     
35144     /**
35145      * @cfg {Boolean} isFitContainer defalut true
35146      */   
35147     isFitContainer : true, 
35148     
35149     /**
35150      * @cfg {Boolean} preventDefault defalut false
35151      */   
35152     preventDefault : false, 
35153     
35154     /**
35155      * @cfg {Boolean} inverse defalut false
35156      */   
35157     maskInverse : false, 
35158     
35159     getAutoCreate : function()
35160     {
35161         if(!this.isFitContainer){
35162             return this.getSplitAutoCreate();
35163         }
35164         
35165         var cls = 'masonry-brick masonry-brick-full';
35166         
35167         if(this.href.length){
35168             cls += ' masonry-brick-link';
35169         }
35170         
35171         if(this.bgimage.length){
35172             cls += ' masonry-brick-image';
35173         }
35174         
35175         if(this.maskInverse){
35176             cls += ' mask-inverse';
35177         }
35178         
35179         if(!this.html.length && !this.maskInverse && !this.videourl.length){
35180             cls += ' enable-mask';
35181         }
35182         
35183         if(this.size){
35184             cls += ' masonry-' + this.size + '-brick';
35185         }
35186         
35187         if(this.placetitle.length){
35188             
35189             switch (this.placetitle) {
35190                 case 'center' :
35191                     cls += ' masonry-center-title';
35192                     break;
35193                 case 'bottom' :
35194                     cls += ' masonry-bottom-title';
35195                     break;
35196                 default:
35197                     break;
35198             }
35199             
35200         } else {
35201             if(!this.html.length && !this.bgimage.length){
35202                 cls += ' masonry-center-title';
35203             }
35204
35205             if(!this.html.length && this.bgimage.length){
35206                 cls += ' masonry-bottom-title';
35207             }
35208         }
35209         
35210         if(this.cls){
35211             cls += ' ' + this.cls;
35212         }
35213         
35214         var cfg = {
35215             tag: (this.href.length) ? 'a' : 'div',
35216             cls: cls,
35217             cn: [
35218                 {
35219                     tag: 'div',
35220                     cls: 'masonry-brick-mask'
35221                 },
35222                 {
35223                     tag: 'div',
35224                     cls: 'masonry-brick-paragraph',
35225                     cn: []
35226                 }
35227             ]
35228         };
35229         
35230         if(this.href.length){
35231             cfg.href = this.href;
35232         }
35233         
35234         var cn = cfg.cn[1].cn;
35235         
35236         if(this.title.length){
35237             cn.push({
35238                 tag: 'h4',
35239                 cls: 'masonry-brick-title',
35240                 html: this.title
35241             });
35242         }
35243         
35244         if(this.html.length){
35245             cn.push({
35246                 tag: 'p',
35247                 cls: 'masonry-brick-text',
35248                 html: this.html
35249             });
35250         }
35251         
35252         if (!this.title.length && !this.html.length) {
35253             cfg.cn[1].cls += ' hide';
35254         }
35255         
35256         if(this.bgimage.length){
35257             cfg.cn.push({
35258                 tag: 'img',
35259                 cls: 'masonry-brick-image-view',
35260                 src: this.bgimage
35261             });
35262         }
35263         
35264         if(this.videourl.length){
35265             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35266             // youtube support only?
35267             cfg.cn.push({
35268                 tag: 'iframe',
35269                 cls: 'masonry-brick-image-view',
35270                 src: vurl,
35271                 frameborder : 0,
35272                 allowfullscreen : true
35273             });
35274         }
35275         
35276         return cfg;
35277         
35278     },
35279     
35280     getSplitAutoCreate : function()
35281     {
35282         var cls = 'masonry-brick masonry-brick-split';
35283         
35284         if(this.href.length){
35285             cls += ' masonry-brick-link';
35286         }
35287         
35288         if(this.bgimage.length){
35289             cls += ' masonry-brick-image';
35290         }
35291         
35292         if(this.size){
35293             cls += ' masonry-' + this.size + '-brick';
35294         }
35295         
35296         switch (this.placetitle) {
35297             case 'center' :
35298                 cls += ' masonry-center-title';
35299                 break;
35300             case 'bottom' :
35301                 cls += ' masonry-bottom-title';
35302                 break;
35303             default:
35304                 if(!this.bgimage.length){
35305                     cls += ' masonry-center-title';
35306                 }
35307
35308                 if(this.bgimage.length){
35309                     cls += ' masonry-bottom-title';
35310                 }
35311                 break;
35312         }
35313         
35314         if(this.cls){
35315             cls += ' ' + this.cls;
35316         }
35317         
35318         var cfg = {
35319             tag: (this.href.length) ? 'a' : 'div',
35320             cls: cls,
35321             cn: [
35322                 {
35323                     tag: 'div',
35324                     cls: 'masonry-brick-split-head',
35325                     cn: [
35326                         {
35327                             tag: 'div',
35328                             cls: 'masonry-brick-paragraph',
35329                             cn: []
35330                         }
35331                     ]
35332                 },
35333                 {
35334                     tag: 'div',
35335                     cls: 'masonry-brick-split-body',
35336                     cn: []
35337                 }
35338             ]
35339         };
35340         
35341         if(this.href.length){
35342             cfg.href = this.href;
35343         }
35344         
35345         if(this.title.length){
35346             cfg.cn[0].cn[0].cn.push({
35347                 tag: 'h4',
35348                 cls: 'masonry-brick-title',
35349                 html: this.title
35350             });
35351         }
35352         
35353         if(this.html.length){
35354             cfg.cn[1].cn.push({
35355                 tag: 'p',
35356                 cls: 'masonry-brick-text',
35357                 html: this.html
35358             });
35359         }
35360
35361         if(this.bgimage.length){
35362             cfg.cn[0].cn.push({
35363                 tag: 'img',
35364                 cls: 'masonry-brick-image-view',
35365                 src: this.bgimage
35366             });
35367         }
35368         
35369         if(this.videourl.length){
35370             var vurl = this.videourl.replace(/https:\/\/youtu\.be/, 'https://www.youtube.com/embed/');
35371             // youtube support only?
35372             cfg.cn[0].cn.cn.push({
35373                 tag: 'iframe',
35374                 cls: 'masonry-brick-image-view',
35375                 src: vurl,
35376                 frameborder : 0,
35377                 allowfullscreen : true
35378             });
35379         }
35380         
35381         return cfg;
35382     },
35383     
35384     initEvents: function() 
35385     {
35386         switch (this.size) {
35387             case 'xs' :
35388                 this.x = 1;
35389                 this.y = 1;
35390                 break;
35391             case 'sm' :
35392                 this.x = 2;
35393                 this.y = 2;
35394                 break;
35395             case 'md' :
35396             case 'md-left' :
35397             case 'md-right' :
35398                 this.x = 3;
35399                 this.y = 3;
35400                 break;
35401             case 'tall' :
35402                 this.x = 2;
35403                 this.y = 3;
35404                 break;
35405             case 'wide' :
35406                 this.x = 3;
35407                 this.y = 2;
35408                 break;
35409             case 'wide-thin' :
35410                 this.x = 3;
35411                 this.y = 1;
35412                 break;
35413                         
35414             default :
35415                 break;
35416         }
35417         
35418         if(Roo.isTouch){
35419             this.el.on('touchstart', this.onTouchStart, this);
35420             this.el.on('touchmove', this.onTouchMove, this);
35421             this.el.on('touchend', this.onTouchEnd, this);
35422             this.el.on('contextmenu', this.onContextMenu, this);
35423         } else {
35424             this.el.on('mouseenter'  ,this.enter, this);
35425             this.el.on('mouseleave', this.leave, this);
35426             this.el.on('click', this.onClick, this);
35427         }
35428         
35429         if (typeof(this.parent().bricks) == 'object' && this.parent().bricks != null) {
35430             this.parent().bricks.push(this);   
35431         }
35432         
35433     },
35434     
35435     onClick: function(e, el)
35436     {
35437         var time = this.endTimer - this.startTimer;
35438         // Roo.log(e.preventDefault());
35439         if(Roo.isTouch){
35440             if(time > 1000){
35441                 e.preventDefault();
35442                 return;
35443             }
35444         }
35445         
35446         if(!this.preventDefault){
35447             return;
35448         }
35449         
35450         e.preventDefault();
35451         
35452         if (this.activeClass != '') {
35453             this.selectBrick();
35454         }
35455         
35456         this.fireEvent('click', this, e);
35457     },
35458     
35459     enter: function(e, el)
35460     {
35461         e.preventDefault();
35462         
35463         if(!this.isFitContainer || this.maskInverse || this.videourl.length){
35464             return;
35465         }
35466         
35467         if(this.bgimage.length && this.html.length){
35468             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35469         }
35470     },
35471     
35472     leave: function(e, el)
35473     {
35474         e.preventDefault();
35475         
35476         if(!this.isFitContainer || this.maskInverse  || this.videourl.length){
35477             return;
35478         }
35479         
35480         if(this.bgimage.length && this.html.length){
35481             this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35482         }
35483     },
35484     
35485     onTouchStart: function(e, el)
35486     {
35487 //        e.preventDefault();
35488         
35489         this.touchmoved = false;
35490         
35491         if(!this.isFitContainer){
35492             return;
35493         }
35494         
35495         if(!this.bgimage.length || !this.html.length){
35496             return;
35497         }
35498         
35499         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0.9, true);
35500         
35501         this.timer = new Date().getTime();
35502         
35503     },
35504     
35505     onTouchMove: function(e, el)
35506     {
35507         this.touchmoved = true;
35508     },
35509     
35510     onContextMenu : function(e,el)
35511     {
35512         e.preventDefault();
35513         e.stopPropagation();
35514         return false;
35515     },
35516     
35517     onTouchEnd: function(e, el)
35518     {
35519 //        e.preventDefault();
35520         
35521         if((new Date().getTime() - this.timer > 1000) || !this.href.length || this.touchmoved){
35522         
35523             this.leave(e,el);
35524             
35525             return;
35526         }
35527         
35528         if(!this.bgimage.length || !this.html.length){
35529             
35530             if(this.href.length){
35531                 window.location.href = this.href;
35532             }
35533             
35534             return;
35535         }
35536         
35537         if(!this.isFitContainer){
35538             return;
35539         }
35540         
35541         this.el.select('.masonry-brick-paragraph', true).first().setOpacity(0, true);
35542         
35543         window.location.href = this.href;
35544     },
35545     
35546     //selection on single brick only
35547     selectBrick : function() {
35548         
35549         if (!this.parentId) {
35550             return;
35551         }
35552         
35553         var m = Roo.bootstrap.LayoutMasonry.get(this.parentId);
35554         var index = m.selectedBrick.indexOf(this.id);
35555         
35556         if ( index > -1) {
35557             m.selectedBrick.splice(index,1);
35558             this.el.removeClass(this.activeClass);
35559             return;
35560         }
35561         
35562         for(var i = 0; i < m.selectedBrick.length; i++) {
35563             var b = Roo.bootstrap.MasonryBrick.get(m.selectedBrick[i]);
35564             b.el.removeClass(b.activeClass);
35565         }
35566         
35567         m.selectedBrick = [];
35568         
35569         m.selectedBrick.push(this.id);
35570         this.el.addClass(this.activeClass);
35571         return;
35572     },
35573     
35574     isSelected : function(){
35575         return this.el.hasClass(this.activeClass);
35576         
35577     }
35578 });
35579
35580 Roo.apply(Roo.bootstrap.MasonryBrick, {
35581     
35582     //groups: {},
35583     groups : new Roo.util.MixedCollection(false, function(o) { return o.el.id; }),
35584      /**
35585     * register a Masonry Brick
35586     * @param {Roo.bootstrap.MasonryBrick} the masonry brick to add
35587     */
35588     
35589     register : function(brick)
35590     {
35591         //this.groups[brick.id] = brick;
35592         this.groups.add(brick.id, brick);
35593     },
35594     /**
35595     * fetch a  masonry brick based on the masonry brick ID
35596     * @param {string} the masonry brick to add
35597     * @returns {Roo.bootstrap.MasonryBrick} the masonry brick
35598     */
35599     
35600     get: function(brick_id) 
35601     {
35602         // if (typeof(this.groups[brick_id]) == 'undefined') {
35603         //     return false;
35604         // }
35605         // return this.groups[brick_id] ;
35606         
35607         if(this.groups.key(brick_id)) {
35608             return this.groups.key(brick_id);
35609         }
35610         
35611         return false;
35612     }
35613     
35614     
35615     
35616 });
35617
35618  /*
35619  * - LGPL
35620  *
35621  * element
35622  * 
35623  */
35624
35625 /**
35626  * @class Roo.bootstrap.Brick
35627  * @extends Roo.bootstrap.Component
35628  * Bootstrap Brick class
35629  * 
35630  * @constructor
35631  * Create a new Brick
35632  * @param {Object} config The config object
35633  */
35634
35635 Roo.bootstrap.Brick = function(config){
35636     Roo.bootstrap.Brick.superclass.constructor.call(this, config);
35637     
35638     this.addEvents({
35639         // raw events
35640         /**
35641          * @event click
35642          * When a Brick is click
35643          * @param {Roo.bootstrap.Brick} this
35644          * @param {Roo.EventObject} e
35645          */
35646         "click" : true
35647     });
35648 };
35649
35650 Roo.extend(Roo.bootstrap.Brick, Roo.bootstrap.Component,  {
35651     
35652     /**
35653      * @cfg {String} title
35654      */   
35655     title : '',
35656     /**
35657      * @cfg {String} html
35658      */   
35659     html : '',
35660     /**
35661      * @cfg {String} bgimage
35662      */   
35663     bgimage : '',
35664     /**
35665      * @cfg {String} cls
35666      */   
35667     cls : '',
35668     /**
35669      * @cfg {String} href
35670      */   
35671     href : '',
35672     /**
35673      * @cfg {String} video
35674      */   
35675     video : '',
35676     /**
35677      * @cfg {Boolean} square
35678      */   
35679     square : true,
35680     
35681     getAutoCreate : function()
35682     {
35683         var cls = 'roo-brick';
35684         
35685         if(this.href.length){
35686             cls += ' roo-brick-link';
35687         }
35688         
35689         if(this.bgimage.length){
35690             cls += ' roo-brick-image';
35691         }
35692         
35693         if(!this.html.length && !this.bgimage.length){
35694             cls += ' roo-brick-center-title';
35695         }
35696         
35697         if(!this.html.length && this.bgimage.length){
35698             cls += ' roo-brick-bottom-title';
35699         }
35700         
35701         if(this.cls){
35702             cls += ' ' + this.cls;
35703         }
35704         
35705         var cfg = {
35706             tag: (this.href.length) ? 'a' : 'div',
35707             cls: cls,
35708             cn: [
35709                 {
35710                     tag: 'div',
35711                     cls: 'roo-brick-paragraph',
35712                     cn: []
35713                 }
35714             ]
35715         };
35716         
35717         if(this.href.length){
35718             cfg.href = this.href;
35719         }
35720         
35721         var cn = cfg.cn[0].cn;
35722         
35723         if(this.title.length){
35724             cn.push({
35725                 tag: 'h4',
35726                 cls: 'roo-brick-title',
35727                 html: this.title
35728             });
35729         }
35730         
35731         if(this.html.length){
35732             cn.push({
35733                 tag: 'p',
35734                 cls: 'roo-brick-text',
35735                 html: this.html
35736             });
35737         } else {
35738             cn.cls += ' hide';
35739         }
35740         
35741         if(this.bgimage.length){
35742             cfg.cn.push({
35743                 tag: 'img',
35744                 cls: 'roo-brick-image-view',
35745                 src: this.bgimage
35746             });
35747         }
35748         
35749         return cfg;
35750     },
35751     
35752     initEvents: function() 
35753     {
35754         if(this.title.length || this.html.length){
35755             this.el.on('mouseenter'  ,this.enter, this);
35756             this.el.on('mouseleave', this.leave, this);
35757         }
35758         
35759         Roo.EventManager.onWindowResize(this.resize, this); 
35760         
35761         if(this.bgimage.length){
35762             this.imageEl = this.el.select('.roo-brick-image-view', true).first();
35763             this.imageEl.on('load', this.onImageLoad, this);
35764             return;
35765         }
35766         
35767         this.resize();
35768     },
35769     
35770     onImageLoad : function()
35771     {
35772         this.resize();
35773     },
35774     
35775     resize : function()
35776     {
35777         var paragraph = this.el.select('.roo-brick-paragraph', true).first();
35778         
35779         paragraph.setHeight(paragraph.getWidth() + paragraph.getPadding('tb'));
35780         
35781         if(this.bgimage.length){
35782             var image = this.el.select('.roo-brick-image-view', true).first();
35783             
35784             image.setWidth(paragraph.getWidth());
35785             
35786             if(this.square){
35787                 image.setHeight(paragraph.getWidth());
35788             }
35789             
35790             this.el.setHeight(image.getHeight());
35791             paragraph.setHeight(image.getHeight());
35792             
35793         }
35794         
35795     },
35796     
35797     enter: function(e, el)
35798     {
35799         e.preventDefault();
35800         
35801         if(this.bgimage.length){
35802             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0.9, true);
35803             this.el.select('.roo-brick-image-view', true).first().setOpacity(0.1, true);
35804         }
35805     },
35806     
35807     leave: function(e, el)
35808     {
35809         e.preventDefault();
35810         
35811         if(this.bgimage.length){
35812             this.el.select('.roo-brick-paragraph', true).first().setOpacity(0, true);
35813             this.el.select('.roo-brick-image-view', true).first().setOpacity(1, true);
35814         }
35815     }
35816     
35817 });
35818
35819  
35820
35821  /*
35822  * - LGPL
35823  *
35824  * Number field 
35825  */
35826
35827 /**
35828  * @class Roo.bootstrap.NumberField
35829  * @extends Roo.bootstrap.Input
35830  * Bootstrap NumberField class
35831  * 
35832  * 
35833  * 
35834  * 
35835  * @constructor
35836  * Create a new NumberField
35837  * @param {Object} config The config object
35838  */
35839
35840 Roo.bootstrap.NumberField = function(config){
35841     Roo.bootstrap.NumberField.superclass.constructor.call(this, config);
35842 };
35843
35844 Roo.extend(Roo.bootstrap.NumberField, Roo.bootstrap.Input, {
35845     
35846     /**
35847      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
35848      */
35849     allowDecimals : true,
35850     /**
35851      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
35852      */
35853     decimalSeparator : ".",
35854     /**
35855      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
35856      */
35857     decimalPrecision : 2,
35858     /**
35859      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
35860      */
35861     allowNegative : true,
35862     
35863     /**
35864      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
35865      */
35866     allowZero: true,
35867     /**
35868      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
35869      */
35870     minValue : Number.NEGATIVE_INFINITY,
35871     /**
35872      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
35873      */
35874     maxValue : Number.MAX_VALUE,
35875     /**
35876      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
35877      */
35878     minText : "The minimum value for this field is {0}",
35879     /**
35880      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
35881      */
35882     maxText : "The maximum value for this field is {0}",
35883     /**
35884      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
35885      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
35886      */
35887     nanText : "{0} is not a valid number",
35888     /**
35889      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
35890      */
35891     thousandsDelimiter : false,
35892     /**
35893      * @cfg {String} valueAlign alignment of value
35894      */
35895     valueAlign : "left",
35896
35897     getAutoCreate : function()
35898     {
35899         var hiddenInput = {
35900             tag: 'input',
35901             type: 'hidden',
35902             id: Roo.id(),
35903             cls: 'hidden-number-input'
35904         };
35905         
35906         if (this.name) {
35907             hiddenInput.name = this.name;
35908         }
35909         
35910         this.name = '';
35911         
35912         var cfg = Roo.bootstrap.NumberField.superclass.getAutoCreate.call(this);
35913         
35914         this.name = hiddenInput.name;
35915         
35916         if(cfg.cn.length > 0) {
35917             cfg.cn.push(hiddenInput);
35918         }
35919         
35920         return cfg;
35921     },
35922
35923     // private
35924     initEvents : function()
35925     {   
35926         Roo.bootstrap.NumberField.superclass.initEvents.call(this);
35927         
35928         var allowed = "0123456789";
35929         
35930         if(this.allowDecimals){
35931             allowed += this.decimalSeparator;
35932         }
35933         
35934         if(this.allowNegative){
35935             allowed += "-";
35936         }
35937         
35938         if(this.thousandsDelimiter) {
35939             allowed += ",";
35940         }
35941         
35942         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
35943         
35944         var keyPress = function(e){
35945             
35946             var k = e.getKey();
35947             
35948             var c = e.getCharCode();
35949             
35950             if(
35951                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
35952                     allowed.indexOf(String.fromCharCode(c)) === -1
35953             ){
35954                 e.stopEvent();
35955                 return;
35956             }
35957             
35958             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
35959                 return;
35960             }
35961             
35962             if(allowed.indexOf(String.fromCharCode(c)) === -1){
35963                 e.stopEvent();
35964             }
35965         };
35966         
35967         this.el.on("keypress", keyPress, this);
35968     },
35969     
35970     validateValue : function(value)
35971     {
35972         
35973         if(!Roo.bootstrap.NumberField.superclass.validateValue.call(this, value)){
35974             return false;
35975         }
35976         
35977         var num = this.parseValue(value);
35978         
35979         if(isNaN(num)){
35980             this.markInvalid(String.format(this.nanText, value));
35981             return false;
35982         }
35983         
35984         if(num < this.minValue){
35985             this.markInvalid(String.format(this.minText, this.minValue));
35986             return false;
35987         }
35988         
35989         if(num > this.maxValue){
35990             this.markInvalid(String.format(this.maxText, this.maxValue));
35991             return false;
35992         }
35993         
35994         return true;
35995     },
35996
35997     getValue : function()
35998     {
35999         var v = this.hiddenEl().getValue();
36000         
36001         return this.fixPrecision(this.parseValue(v));
36002     },
36003
36004     parseValue : function(value)
36005     {
36006         if(this.thousandsDelimiter) {
36007             value += "";
36008             r = new RegExp(",", "g");
36009             value = value.replace(r, "");
36010         }
36011         
36012         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
36013         return isNaN(value) ? '' : value;
36014     },
36015
36016     fixPrecision : function(value)
36017     {
36018         if(this.thousandsDelimiter) {
36019             value += "";
36020             r = new RegExp(",", "g");
36021             value = value.replace(r, "");
36022         }
36023         
36024         var nan = isNaN(value);
36025         
36026         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
36027             return nan ? '' : value;
36028         }
36029         return parseFloat(value).toFixed(this.decimalPrecision);
36030     },
36031
36032     setValue : function(v)
36033     {
36034         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
36035         
36036         this.value = v;
36037         
36038         if(this.rendered){
36039             
36040             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
36041             
36042             this.inputEl().dom.value = (v == '') ? '' :
36043                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
36044             
36045             if(!this.allowZero && v === '0') {
36046                 this.hiddenEl().dom.value = '';
36047                 this.inputEl().dom.value = '';
36048             }
36049             
36050             this.validate();
36051         }
36052     },
36053
36054     decimalPrecisionFcn : function(v)
36055     {
36056         return Math.floor(v);
36057     },
36058
36059     beforeBlur : function()
36060     {
36061         var v = this.parseValue(this.getRawValue());
36062         
36063         if(v || v === 0 || v === ''){
36064             this.setValue(v);
36065         }
36066     },
36067     
36068     hiddenEl : function()
36069     {
36070         return this.el.select('input.hidden-number-input',true).first();
36071     }
36072     
36073 });
36074
36075  
36076
36077 /*
36078 * Licence: LGPL
36079 */
36080
36081 /**
36082  * @class Roo.bootstrap.DocumentSlider
36083  * @extends Roo.bootstrap.Component
36084  * Bootstrap DocumentSlider class
36085  * 
36086  * @constructor
36087  * Create a new DocumentViewer
36088  * @param {Object} config The config object
36089  */
36090
36091 Roo.bootstrap.DocumentSlider = function(config){
36092     Roo.bootstrap.DocumentSlider.superclass.constructor.call(this, config);
36093     
36094     this.files = [];
36095     
36096     this.addEvents({
36097         /**
36098          * @event initial
36099          * Fire after initEvent
36100          * @param {Roo.bootstrap.DocumentSlider} this
36101          */
36102         "initial" : true,
36103         /**
36104          * @event update
36105          * Fire after update
36106          * @param {Roo.bootstrap.DocumentSlider} this
36107          */
36108         "update" : true,
36109         /**
36110          * @event click
36111          * Fire after click
36112          * @param {Roo.bootstrap.DocumentSlider} this
36113          */
36114         "click" : true
36115     });
36116 };
36117
36118 Roo.extend(Roo.bootstrap.DocumentSlider, Roo.bootstrap.Component,  {
36119     
36120     files : false,
36121     
36122     indicator : 0,
36123     
36124     getAutoCreate : function()
36125     {
36126         var cfg = {
36127             tag : 'div',
36128             cls : 'roo-document-slider',
36129             cn : [
36130                 {
36131                     tag : 'div',
36132                     cls : 'roo-document-slider-header',
36133                     cn : [
36134                         {
36135                             tag : 'div',
36136                             cls : 'roo-document-slider-header-title'
36137                         }
36138                     ]
36139                 },
36140                 {
36141                     tag : 'div',
36142                     cls : 'roo-document-slider-body',
36143                     cn : [
36144                         {
36145                             tag : 'div',
36146                             cls : 'roo-document-slider-prev',
36147                             cn : [
36148                                 {
36149                                     tag : 'i',
36150                                     cls : 'fa fa-chevron-left'
36151                                 }
36152                             ]
36153                         },
36154                         {
36155                             tag : 'div',
36156                             cls : 'roo-document-slider-thumb',
36157                             cn : [
36158                                 {
36159                                     tag : 'img',
36160                                     cls : 'roo-document-slider-image'
36161                                 }
36162                             ]
36163                         },
36164                         {
36165                             tag : 'div',
36166                             cls : 'roo-document-slider-next',
36167                             cn : [
36168                                 {
36169                                     tag : 'i',
36170                                     cls : 'fa fa-chevron-right'
36171                                 }
36172                             ]
36173                         }
36174                     ]
36175                 }
36176             ]
36177         };
36178         
36179         return cfg;
36180     },
36181     
36182     initEvents : function()
36183     {
36184         this.headerEl = this.el.select('.roo-document-slider-header', true).first();
36185         this.headerEl.setVisibilityMode(Roo.Element.DISPLAY);
36186         
36187         this.titleEl = this.el.select('.roo-document-slider-header .roo-document-slider-header-title', true).first();
36188         this.titleEl.setVisibilityMode(Roo.Element.DISPLAY);
36189         
36190         this.bodyEl = this.el.select('.roo-document-slider-body', true).first();
36191         this.bodyEl.setVisibilityMode(Roo.Element.DISPLAY);
36192         
36193         this.thumbEl = this.el.select('.roo-document-slider-thumb', true).first();
36194         this.thumbEl.setVisibilityMode(Roo.Element.DISPLAY);
36195         
36196         this.imageEl = this.el.select('.roo-document-slider-image', true).first();
36197         this.imageEl.setVisibilityMode(Roo.Element.DISPLAY);
36198         
36199         this.prevIndicator = this.el.select('.roo-document-slider-prev i', true).first();
36200         this.prevIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36201         
36202         this.nextIndicator = this.el.select('.roo-document-slider-next i', true).first();
36203         this.nextIndicator.setVisibilityMode(Roo.Element.DISPLAY);
36204         
36205         this.thumbEl.on('click', this.onClick, this);
36206         
36207         this.prevIndicator.on('click', this.prev, this);
36208         
36209         this.nextIndicator.on('click', this.next, this);
36210         
36211     },
36212     
36213     initial : function()
36214     {
36215         if(this.files.length){
36216             this.indicator = 1;
36217             this.update()
36218         }
36219         
36220         this.fireEvent('initial', this);
36221     },
36222     
36223     update : function()
36224     {
36225         this.imageEl.attr('src', this.files[this.indicator - 1]);
36226         
36227         this.titleEl.dom.innerHTML = String.format('{0} / {1}', this.indicator, this.files.length);
36228         
36229         this.prevIndicator.show();
36230         
36231         if(this.indicator == 1){
36232             this.prevIndicator.hide();
36233         }
36234         
36235         this.nextIndicator.show();
36236         
36237         if(this.indicator == this.files.length){
36238             this.nextIndicator.hide();
36239         }
36240         
36241         this.thumbEl.scrollTo('top');
36242         
36243         this.fireEvent('update', this);
36244     },
36245     
36246     onClick : function(e)
36247     {
36248         e.preventDefault();
36249         
36250         this.fireEvent('click', this);
36251     },
36252     
36253     prev : function(e)
36254     {
36255         e.preventDefault();
36256         
36257         this.indicator = Math.max(1, this.indicator - 1);
36258         
36259         this.update();
36260     },
36261     
36262     next : function(e)
36263     {
36264         e.preventDefault();
36265         
36266         this.indicator = Math.min(this.files.length, this.indicator + 1);
36267         
36268         this.update();
36269     }
36270 });
36271 /*
36272  * - LGPL
36273  *
36274  * RadioSet
36275  *
36276  *
36277  */
36278
36279 /**
36280  * @class Roo.bootstrap.RadioSet
36281  * @extends Roo.bootstrap.Input
36282  * Bootstrap RadioSet class
36283  * @cfg {String} indicatorpos (left|right) default left
36284  * @cfg {Boolean} inline (true|false) inline the element (default true)
36285  * @cfg {String} weight (primary|warning|info|danger|success) The text that appears beside the radio
36286  * @constructor
36287  * Create a new RadioSet
36288  * @param {Object} config The config object
36289  */
36290
36291 Roo.bootstrap.RadioSet = function(config){
36292     
36293     Roo.bootstrap.RadioSet.superclass.constructor.call(this, config);
36294     
36295     this.radioes = [];
36296     
36297     Roo.bootstrap.RadioSet.register(this);
36298     
36299     this.addEvents({
36300         /**
36301         * @event check
36302         * Fires when the element is checked or unchecked.
36303         * @param {Roo.bootstrap.RadioSet} this This radio
36304         * @param {Roo.bootstrap.Radio} item The checked item
36305         */
36306        check : true,
36307        /**
36308         * @event click
36309         * Fires when the element is click.
36310         * @param {Roo.bootstrap.RadioSet} this This radio set
36311         * @param {Roo.bootstrap.Radio} item The checked item
36312         * @param {Roo.EventObject} e The event object
36313         */
36314        click : true
36315     });
36316     
36317 };
36318
36319 Roo.extend(Roo.bootstrap.RadioSet, Roo.bootstrap.Input,  {
36320
36321     radioes : false,
36322     
36323     inline : true,
36324     
36325     weight : '',
36326     
36327     indicatorpos : 'left',
36328     
36329     getAutoCreate : function()
36330     {
36331         var label = {
36332             tag : 'label',
36333             cls : 'roo-radio-set-label',
36334             cn : [
36335                 {
36336                     tag : 'span',
36337                     html : this.fieldLabel
36338                 }
36339             ]
36340         };
36341         if (Roo.bootstrap.version == 3) {
36342             
36343             
36344             if(this.indicatorpos == 'left'){
36345                 label.cn.unshift({
36346                     tag : 'i',
36347                     cls : 'roo-required-indicator left-indicator text-danger fa fa-lg fa-star',
36348                     tooltip : 'This field is required'
36349                 });
36350             } else {
36351                 label.cn.push({
36352                     tag : 'i',
36353                     cls : 'roo-required-indicator right-indicator text-danger fa fa-lg fa-star',
36354                     tooltip : 'This field is required'
36355                 });
36356             }
36357         }
36358         var items = {
36359             tag : 'div',
36360             cls : 'roo-radio-set-items'
36361         };
36362         
36363         var align = (!this.labelAlign) ? this.parentLabelAlign() : this.labelAlign;
36364         
36365         if (align === 'left' && this.fieldLabel.length) {
36366             
36367             items = {
36368                 cls : "roo-radio-set-right", 
36369                 cn: [
36370                     items
36371                 ]
36372             };
36373             
36374             if(this.labelWidth > 12){
36375                 label.style = "width: " + this.labelWidth + 'px';
36376             }
36377             
36378             if(this.labelWidth < 13 && this.labelmd == 0){
36379                 this.labelmd = this.labelWidth;
36380             }
36381             
36382             if(this.labellg > 0){
36383                 label.cls += ' col-lg-' + this.labellg;
36384                 items.cls += ' col-lg-' + (12 - this.labellg);
36385             }
36386             
36387             if(this.labelmd > 0){
36388                 label.cls += ' col-md-' + this.labelmd;
36389                 items.cls += ' col-md-' + (12 - this.labelmd);
36390             }
36391             
36392             if(this.labelsm > 0){
36393                 label.cls += ' col-sm-' + this.labelsm;
36394                 items.cls += ' col-sm-' + (12 - this.labelsm);
36395             }
36396             
36397             if(this.labelxs > 0){
36398                 label.cls += ' col-xs-' + this.labelxs;
36399                 items.cls += ' col-xs-' + (12 - this.labelxs);
36400             }
36401         }
36402         
36403         var cfg = {
36404             tag : 'div',
36405             cls : 'roo-radio-set',
36406             cn : [
36407                 {
36408                     tag : 'input',
36409                     cls : 'roo-radio-set-input',
36410                     type : 'hidden',
36411                     name : this.name,
36412                     value : this.value ? this.value :  ''
36413                 },
36414                 label,
36415                 items
36416             ]
36417         };
36418         
36419         if(this.weight.length){
36420             cfg.cls += ' roo-radio-' + this.weight;
36421         }
36422         
36423         if(this.inline) {
36424             cfg.cls += ' roo-radio-set-inline';
36425         }
36426         
36427         var settings=this;
36428         ['xs','sm','md','lg'].map(function(size){
36429             if (settings[size]) {
36430                 cfg.cls += ' col-' + size + '-' + settings[size];
36431             }
36432         });
36433         
36434         return cfg;
36435         
36436     },
36437
36438     initEvents : function()
36439     {
36440         this.labelEl = this.el.select('.roo-radio-set-label', true).first();
36441         this.labelEl.setVisibilityMode(Roo.Element.DISPLAY);
36442         
36443         if(!this.fieldLabel.length){
36444             this.labelEl.hide();
36445         }
36446         
36447         this.itemsEl = this.el.select('.roo-radio-set-items', true).first();
36448         this.itemsEl.setVisibilityMode(Roo.Element.DISPLAY);
36449         
36450         this.indicator = this.indicatorEl();
36451         
36452         if(this.indicator){
36453             this.indicator.addClass('invisible');
36454         }
36455         
36456         this.originalValue = this.getValue();
36457         
36458     },
36459     
36460     inputEl: function ()
36461     {
36462         return this.el.select('.roo-radio-set-input', true).first();
36463     },
36464     
36465     getChildContainer : function()
36466     {
36467         return this.itemsEl;
36468     },
36469     
36470     register : function(item)
36471     {
36472         this.radioes.push(item);
36473         
36474     },
36475     
36476     validate : function()
36477     {   
36478         if(this.getVisibilityEl().hasClass('hidden')){
36479             return true;
36480         }
36481         
36482         var valid = false;
36483         
36484         Roo.each(this.radioes, function(i){
36485             if(!i.checked){
36486                 return;
36487             }
36488             
36489             valid = true;
36490             return false;
36491         });
36492         
36493         if(this.allowBlank) {
36494             return true;
36495         }
36496         
36497         if(this.disabled || valid){
36498             this.markValid();
36499             return true;
36500         }
36501         
36502         this.markInvalid();
36503         return false;
36504         
36505     },
36506     
36507     markValid : function()
36508     {
36509         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36510             this.indicatorEl().removeClass('visible');
36511             this.indicatorEl().addClass('invisible');
36512         }
36513         
36514         
36515         if (Roo.bootstrap.version == 3) {
36516             this.el.removeClass([this.invalidClass, this.validClass]);
36517             this.el.addClass(this.validClass);
36518         } else {
36519             this.el.removeClass(['is-invalid','is-valid']);
36520             this.el.addClass(['is-valid']);
36521         }
36522         this.fireEvent('valid', this);
36523     },
36524     
36525     markInvalid : function(msg)
36526     {
36527         if(this.allowBlank || this.disabled){
36528             return;
36529         }
36530         
36531         if(this.labelEl.isVisible(true) && this.indicatorEl()){
36532             this.indicatorEl().removeClass('invisible');
36533             this.indicatorEl().addClass('visible');
36534         }
36535         if (Roo.bootstrap.version == 3) {
36536             this.el.removeClass([this.invalidClass, this.validClass]);
36537             this.el.addClass(this.invalidClass);
36538         } else {
36539             this.el.removeClass(['is-invalid','is-valid']);
36540             this.el.addClass(['is-invalid']);
36541         }
36542         
36543         this.fireEvent('invalid', this, msg);
36544         
36545     },
36546     
36547     setValue : function(v, suppressEvent)
36548     {   
36549         if(this.value === v){
36550             return;
36551         }
36552         
36553         this.value = v;
36554         
36555         if(this.rendered){
36556             this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
36557         }
36558         
36559         Roo.each(this.radioes, function(i){
36560             i.checked = false;
36561             i.el.removeClass('checked');
36562         });
36563         
36564         Roo.each(this.radioes, function(i){
36565             
36566             if(i.value === v || i.value.toString() === v.toString()){
36567                 i.checked = true;
36568                 i.el.addClass('checked');
36569                 
36570                 if(suppressEvent !== true){
36571                     this.fireEvent('check', this, i);
36572                 }
36573                 
36574                 return false;
36575             }
36576             
36577         }, this);
36578         
36579         this.validate();
36580     },
36581     
36582     clearInvalid : function(){
36583         
36584         if(!this.el || this.preventMark){
36585             return;
36586         }
36587         
36588         this.el.removeClass([this.invalidClass]);
36589         
36590         this.fireEvent('valid', this);
36591     }
36592     
36593 });
36594
36595 Roo.apply(Roo.bootstrap.RadioSet, {
36596     
36597     groups: {},
36598     
36599     register : function(set)
36600     {
36601         this.groups[set.name] = set;
36602     },
36603     
36604     get: function(name) 
36605     {
36606         if (typeof(this.groups[name]) == 'undefined') {
36607             return false;
36608         }
36609         
36610         return this.groups[name] ;
36611     }
36612     
36613 });
36614 /*
36615  * Based on:
36616  * Ext JS Library 1.1.1
36617  * Copyright(c) 2006-2007, Ext JS, LLC.
36618  *
36619  * Originally Released Under LGPL - original licence link has changed is not relivant.
36620  *
36621  * Fork - LGPL
36622  * <script type="text/javascript">
36623  */
36624
36625
36626 /**
36627  * @class Roo.bootstrap.SplitBar
36628  * @extends Roo.util.Observable
36629  * Creates draggable splitter bar functionality from two elements (element to be dragged and element to be resized).
36630  * <br><br>
36631  * Usage:
36632  * <pre><code>
36633 var split = new Roo.bootstrap.SplitBar("elementToDrag", "elementToSize",
36634                    Roo.bootstrap.SplitBar.HORIZONTAL, Roo.bootstrap.SplitBar.LEFT);
36635 split.setAdapter(new Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter("container"));
36636 split.minSize = 100;
36637 split.maxSize = 600;
36638 split.animate = true;
36639 split.on('moved', splitterMoved);
36640 </code></pre>
36641  * @constructor
36642  * Create a new SplitBar
36643  * @config {String/HTMLElement/Roo.Element} dragElement The element to be dragged and act as the SplitBar. 
36644  * @config {String/HTMLElement/Roo.Element} resizingElement The element to be resized based on where the SplitBar element is dragged 
36645  * @config {Number} orientation (optional) Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36646  * @config {Number} placement (optional) Either Roo.bootstrap.SplitBar.LEFT or Roo.bootstrap.SplitBar.RIGHT for horizontal or  
36647                         Roo.bootstrap.SplitBar.TOP or Roo.bootstrap.SplitBar.BOTTOM for vertical. (By default, this is determined automatically by the initial
36648                         position of the SplitBar).
36649  */
36650 Roo.bootstrap.SplitBar = function(cfg){
36651     
36652     /** @private */
36653     
36654     //{
36655     //  dragElement : elm
36656     //  resizingElement: el,
36657         // optional..
36658     //    orientation : Either Roo.bootstrap.SplitBar.HORIZONTAL
36659     //    placement : Roo.bootstrap.SplitBar.LEFT  ,
36660         // existingProxy ???
36661     //}
36662     
36663     this.el = Roo.get(cfg.dragElement, true);
36664     this.el.dom.unselectable = "on";
36665     /** @private */
36666     this.resizingEl = Roo.get(cfg.resizingElement, true);
36667
36668     /**
36669      * @private
36670      * The orientation of the split. Either Roo.bootstrap.SplitBar.HORIZONTAL or Roo.bootstrap.SplitBar.VERTICAL. (Defaults to HORIZONTAL)
36671      * Note: If this is changed after creating the SplitBar, the placement property must be manually updated
36672      * @type Number
36673      */
36674     this.orientation = cfg.orientation || Roo.bootstrap.SplitBar.HORIZONTAL;
36675     
36676     /**
36677      * The minimum size of the resizing element. (Defaults to 0)
36678      * @type Number
36679      */
36680     this.minSize = 0;
36681     
36682     /**
36683      * The maximum size of the resizing element. (Defaults to 2000)
36684      * @type Number
36685      */
36686     this.maxSize = 2000;
36687     
36688     /**
36689      * Whether to animate the transition to the new size
36690      * @type Boolean
36691      */
36692     this.animate = false;
36693     
36694     /**
36695      * Whether to create a transparent shim that overlays the page when dragging, enables dragging across iframes.
36696      * @type Boolean
36697      */
36698     this.useShim = false;
36699     
36700     /** @private */
36701     this.shim = null;
36702     
36703     if(!cfg.existingProxy){
36704         /** @private */
36705         this.proxy = Roo.bootstrap.SplitBar.createProxy(this.orientation);
36706     }else{
36707         this.proxy = Roo.get(cfg.existingProxy).dom;
36708     }
36709     /** @private */
36710     this.dd = new Roo.dd.DDProxy(this.el.dom.id, "XSplitBars", {dragElId : this.proxy.id});
36711     
36712     /** @private */
36713     this.dd.b4StartDrag = this.onStartProxyDrag.createDelegate(this);
36714     
36715     /** @private */
36716     this.dd.endDrag = this.onEndProxyDrag.createDelegate(this);
36717     
36718     /** @private */
36719     this.dragSpecs = {};
36720     
36721     /**
36722      * @private The adapter to use to positon and resize elements
36723      */
36724     this.adapter = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36725     this.adapter.init(this);
36726     
36727     if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36728         /** @private */
36729         this.placement = cfg.placement || (this.el.getX() > this.resizingEl.getX() ? Roo.bootstrap.SplitBar.LEFT : Roo.bootstrap.SplitBar.RIGHT);
36730         this.el.addClass("roo-splitbar-h");
36731     }else{
36732         /** @private */
36733         this.placement = cfg.placement || (this.el.getY() > this.resizingEl.getY() ? Roo.bootstrap.SplitBar.TOP : Roo.bootstrap.SplitBar.BOTTOM);
36734         this.el.addClass("roo-splitbar-v");
36735     }
36736     
36737     this.addEvents({
36738         /**
36739          * @event resize
36740          * Fires when the splitter is moved (alias for {@link #event-moved})
36741          * @param {Roo.bootstrap.SplitBar} this
36742          * @param {Number} newSize the new width or height
36743          */
36744         "resize" : true,
36745         /**
36746          * @event moved
36747          * Fires when the splitter is moved
36748          * @param {Roo.bootstrap.SplitBar} this
36749          * @param {Number} newSize the new width or height
36750          */
36751         "moved" : true,
36752         /**
36753          * @event beforeresize
36754          * Fires before the splitter is dragged
36755          * @param {Roo.bootstrap.SplitBar} this
36756          */
36757         "beforeresize" : true,
36758
36759         "beforeapply" : true
36760     });
36761
36762     Roo.util.Observable.call(this);
36763 };
36764
36765 Roo.extend(Roo.bootstrap.SplitBar, Roo.util.Observable, {
36766     onStartProxyDrag : function(x, y){
36767         this.fireEvent("beforeresize", this);
36768         if(!this.overlay){
36769             var o = Roo.DomHelper.insertFirst(document.body,  {cls: "roo-drag-overlay", html: "&#160;"}, true);
36770             o.unselectable();
36771             o.enableDisplayMode("block");
36772             // all splitbars share the same overlay
36773             Roo.bootstrap.SplitBar.prototype.overlay = o;
36774         }
36775         this.overlay.setSize(Roo.lib.Dom.getViewWidth(true), Roo.lib.Dom.getViewHeight(true));
36776         this.overlay.show();
36777         Roo.get(this.proxy).setDisplayed("block");
36778         var size = this.adapter.getElementSize(this);
36779         this.activeMinSize = this.getMinimumSize();;
36780         this.activeMaxSize = this.getMaximumSize();;
36781         var c1 = size - this.activeMinSize;
36782         var c2 = Math.max(this.activeMaxSize - size, 0);
36783         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36784             this.dd.resetConstraints();
36785             this.dd.setXConstraint(
36786                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c1 : c2, 
36787                 this.placement == Roo.bootstrap.SplitBar.LEFT ? c2 : c1
36788             );
36789             this.dd.setYConstraint(0, 0);
36790         }else{
36791             this.dd.resetConstraints();
36792             this.dd.setXConstraint(0, 0);
36793             this.dd.setYConstraint(
36794                 this.placement == Roo.bootstrap.SplitBar.TOP ? c1 : c2, 
36795                 this.placement == Roo.bootstrap.SplitBar.TOP ? c2 : c1
36796             );
36797          }
36798         this.dragSpecs.startSize = size;
36799         this.dragSpecs.startPoint = [x, y];
36800         Roo.dd.DDProxy.prototype.b4StartDrag.call(this.dd, x, y);
36801     },
36802     
36803     /** 
36804      * @private Called after the drag operation by the DDProxy
36805      */
36806     onEndProxyDrag : function(e){
36807         Roo.get(this.proxy).setDisplayed(false);
36808         var endPoint = Roo.lib.Event.getXY(e);
36809         if(this.overlay){
36810             this.overlay.hide();
36811         }
36812         var newSize;
36813         if(this.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36814             newSize = this.dragSpecs.startSize + 
36815                 (this.placement == Roo.bootstrap.SplitBar.LEFT ?
36816                     endPoint[0] - this.dragSpecs.startPoint[0] :
36817                     this.dragSpecs.startPoint[0] - endPoint[0]
36818                 );
36819         }else{
36820             newSize = this.dragSpecs.startSize + 
36821                 (this.placement == Roo.bootstrap.SplitBar.TOP ?
36822                     endPoint[1] - this.dragSpecs.startPoint[1] :
36823                     this.dragSpecs.startPoint[1] - endPoint[1]
36824                 );
36825         }
36826         newSize = Math.min(Math.max(newSize, this.activeMinSize), this.activeMaxSize);
36827         if(newSize != this.dragSpecs.startSize){
36828             if(this.fireEvent('beforeapply', this, newSize) !== false){
36829                 this.adapter.setElementSize(this, newSize);
36830                 this.fireEvent("moved", this, newSize);
36831                 this.fireEvent("resize", this, newSize);
36832             }
36833         }
36834     },
36835     
36836     /**
36837      * Get the adapter this SplitBar uses
36838      * @return The adapter object
36839      */
36840     getAdapter : function(){
36841         return this.adapter;
36842     },
36843     
36844     /**
36845      * Set the adapter this SplitBar uses
36846      * @param {Object} adapter A SplitBar adapter object
36847      */
36848     setAdapter : function(adapter){
36849         this.adapter = adapter;
36850         this.adapter.init(this);
36851     },
36852     
36853     /**
36854      * Gets the minimum size for the resizing element
36855      * @return {Number} The minimum size
36856      */
36857     getMinimumSize : function(){
36858         return this.minSize;
36859     },
36860     
36861     /**
36862      * Sets the minimum size for the resizing element
36863      * @param {Number} minSize The minimum size
36864      */
36865     setMinimumSize : function(minSize){
36866         this.minSize = minSize;
36867     },
36868     
36869     /**
36870      * Gets the maximum size for the resizing element
36871      * @return {Number} The maximum size
36872      */
36873     getMaximumSize : function(){
36874         return this.maxSize;
36875     },
36876     
36877     /**
36878      * Sets the maximum size for the resizing element
36879      * @param {Number} maxSize The maximum size
36880      */
36881     setMaximumSize : function(maxSize){
36882         this.maxSize = maxSize;
36883     },
36884     
36885     /**
36886      * Sets the initialize size for the resizing element
36887      * @param {Number} size The initial size
36888      */
36889     setCurrentSize : function(size){
36890         var oldAnimate = this.animate;
36891         this.animate = false;
36892         this.adapter.setElementSize(this, size);
36893         this.animate = oldAnimate;
36894     },
36895     
36896     /**
36897      * Destroy this splitbar. 
36898      * @param {Boolean} removeEl True to remove the element
36899      */
36900     destroy : function(removeEl){
36901         if(this.shim){
36902             this.shim.remove();
36903         }
36904         this.dd.unreg();
36905         this.proxy.parentNode.removeChild(this.proxy);
36906         if(removeEl){
36907             this.el.remove();
36908         }
36909     }
36910 });
36911
36912 /**
36913  * @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.
36914  */
36915 Roo.bootstrap.SplitBar.createProxy = function(dir){
36916     var proxy = new Roo.Element(document.createElement("div"));
36917     proxy.unselectable();
36918     var cls = 'roo-splitbar-proxy';
36919     proxy.addClass(cls + ' ' + (dir == Roo.bootstrap.SplitBar.HORIZONTAL ? cls +'-h' : cls + '-v'));
36920     document.body.appendChild(proxy.dom);
36921     return proxy.dom;
36922 };
36923
36924 /** 
36925  * @class Roo.bootstrap.SplitBar.BasicLayoutAdapter
36926  * Default Adapter. It assumes the splitter and resizing element are not positioned
36927  * elements and only gets/sets the width of the element. Generally used for table based layouts.
36928  */
36929 Roo.bootstrap.SplitBar.BasicLayoutAdapter = function(){
36930 };
36931
36932 Roo.bootstrap.SplitBar.BasicLayoutAdapter.prototype = {
36933     // do nothing for now
36934     init : function(s){
36935     
36936     },
36937     /**
36938      * Called before drag operations to get the current size of the resizing element. 
36939      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36940      */
36941      getElementSize : function(s){
36942         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36943             return s.resizingEl.getWidth();
36944         }else{
36945             return s.resizingEl.getHeight();
36946         }
36947     },
36948     
36949     /**
36950      * Called after drag operations to set the size of the resizing element.
36951      * @param {Roo.bootstrap.SplitBar} s The SplitBar using this adapter
36952      * @param {Number} newSize The new size to set
36953      * @param {Function} onComplete A function to be invoked when resizing is complete
36954      */
36955     setElementSize : function(s, newSize, onComplete){
36956         if(s.orientation == Roo.bootstrap.SplitBar.HORIZONTAL){
36957             if(!s.animate){
36958                 s.resizingEl.setWidth(newSize);
36959                 if(onComplete){
36960                     onComplete(s, newSize);
36961                 }
36962             }else{
36963                 s.resizingEl.setWidth(newSize, true, .1, onComplete, 'easeOut');
36964             }
36965         }else{
36966             
36967             if(!s.animate){
36968                 s.resizingEl.setHeight(newSize);
36969                 if(onComplete){
36970                     onComplete(s, newSize);
36971                 }
36972             }else{
36973                 s.resizingEl.setHeight(newSize, true, .1, onComplete, 'easeOut');
36974             }
36975         }
36976     }
36977 };
36978
36979 /** 
36980  *@class Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter
36981  * @extends Roo.bootstrap.SplitBar.BasicLayoutAdapter
36982  * Adapter that  moves the splitter element to align with the resized sizing element. 
36983  * Used with an absolute positioned SplitBar.
36984  * @param {String/HTMLElement/Roo.Element} container The container that wraps around the absolute positioned content. If it's
36985  * document.body, make sure you assign an id to the body element.
36986  */
36987 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter = function(container){
36988     this.basic = new Roo.bootstrap.SplitBar.BasicLayoutAdapter();
36989     this.container = Roo.get(container);
36990 };
36991
36992 Roo.bootstrap.SplitBar.AbsoluteLayoutAdapter.prototype = {
36993     init : function(s){
36994         this.basic.init(s);
36995     },
36996     
36997     getElementSize : function(s){
36998         return this.basic.getElementSize(s);
36999     },
37000     
37001     setElementSize : function(s, newSize, onComplete){
37002         this.basic.setElementSize(s, newSize, this.moveSplitter.createDelegate(this, [s]));
37003     },
37004     
37005     moveSplitter : function(s){
37006         var yes = Roo.bootstrap.SplitBar;
37007         switch(s.placement){
37008             case yes.LEFT:
37009                 s.el.setX(s.resizingEl.getRight());
37010                 break;
37011             case yes.RIGHT:
37012                 s.el.setStyle("right", (this.container.getWidth() - s.resizingEl.getLeft()) + "px");
37013                 break;
37014             case yes.TOP:
37015                 s.el.setY(s.resizingEl.getBottom());
37016                 break;
37017             case yes.BOTTOM:
37018                 s.el.setY(s.resizingEl.getTop() - s.el.getHeight());
37019                 break;
37020         }
37021     }
37022 };
37023
37024 /**
37025  * Orientation constant - Create a vertical SplitBar
37026  * @static
37027  * @type Number
37028  */
37029 Roo.bootstrap.SplitBar.VERTICAL = 1;
37030
37031 /**
37032  * Orientation constant - Create a horizontal SplitBar
37033  * @static
37034  * @type Number
37035  */
37036 Roo.bootstrap.SplitBar.HORIZONTAL = 2;
37037
37038 /**
37039  * Placement constant - The resizing element is to the left of the splitter element
37040  * @static
37041  * @type Number
37042  */
37043 Roo.bootstrap.SplitBar.LEFT = 1;
37044
37045 /**
37046  * Placement constant - The resizing element is to the right of the splitter element
37047  * @static
37048  * @type Number
37049  */
37050 Roo.bootstrap.SplitBar.RIGHT = 2;
37051
37052 /**
37053  * Placement constant - The resizing element is positioned above the splitter element
37054  * @static
37055  * @type Number
37056  */
37057 Roo.bootstrap.SplitBar.TOP = 3;
37058
37059 /**
37060  * Placement constant - The resizing element is positioned under splitter element
37061  * @static
37062  * @type Number
37063  */
37064 Roo.bootstrap.SplitBar.BOTTOM = 4;
37065 Roo.namespace("Roo.bootstrap.layout");/*
37066  * Based on:
37067  * Ext JS Library 1.1.1
37068  * Copyright(c) 2006-2007, Ext JS, LLC.
37069  *
37070  * Originally Released Under LGPL - original licence link has changed is not relivant.
37071  *
37072  * Fork - LGPL
37073  * <script type="text/javascript">
37074  */
37075
37076 /**
37077  * @class Roo.bootstrap.layout.Manager
37078  * @extends Roo.bootstrap.Component
37079  * Base class for layout managers.
37080  */
37081 Roo.bootstrap.layout.Manager = function(config)
37082 {
37083     Roo.bootstrap.layout.Manager.superclass.constructor.call(this,config);
37084
37085
37086
37087
37088
37089     /** false to disable window resize monitoring @type Boolean */
37090     this.monitorWindowResize = true;
37091     this.regions = {};
37092     this.addEvents({
37093         /**
37094          * @event layout
37095          * Fires when a layout is performed.
37096          * @param {Roo.LayoutManager} this
37097          */
37098         "layout" : true,
37099         /**
37100          * @event regionresized
37101          * Fires when the user resizes a region.
37102          * @param {Roo.LayoutRegion} region The resized region
37103          * @param {Number} newSize The new size (width for east/west, height for north/south)
37104          */
37105         "regionresized" : true,
37106         /**
37107          * @event regioncollapsed
37108          * Fires when a region is collapsed.
37109          * @param {Roo.LayoutRegion} region The collapsed region
37110          */
37111         "regioncollapsed" : true,
37112         /**
37113          * @event regionexpanded
37114          * Fires when a region is expanded.
37115          * @param {Roo.LayoutRegion} region The expanded region
37116          */
37117         "regionexpanded" : true
37118     });
37119     this.updating = false;
37120
37121     if (config.el) {
37122         this.el = Roo.get(config.el);
37123         this.initEvents();
37124     }
37125
37126 };
37127
37128 Roo.extend(Roo.bootstrap.layout.Manager, Roo.bootstrap.Component, {
37129
37130
37131     regions : null,
37132
37133     monitorWindowResize : true,
37134
37135
37136     updating : false,
37137
37138
37139     onRender : function(ct, position)
37140     {
37141         if(!this.el){
37142             this.el = Roo.get(ct);
37143             this.initEvents();
37144         }
37145         //this.fireEvent('render',this);
37146     },
37147
37148
37149     initEvents: function()
37150     {
37151
37152
37153         // ie scrollbar fix
37154         if(this.el.dom == document.body && Roo.isIE && !config.allowScroll){
37155             document.body.scroll = "no";
37156         }else if(this.el.dom != document.body && this.el.getStyle('position') == 'static'){
37157             this.el.position('relative');
37158         }
37159         this.id = this.el.id;
37160         this.el.addClass("roo-layout-container");
37161         Roo.EventManager.onWindowResize(this.onWindowResize, this, true);
37162         if(this.el.dom != document.body ) {
37163             this.el.on('resize', this.layout,this);
37164             this.el.on('show', this.layout,this);
37165         }
37166
37167     },
37168
37169     /**
37170      * Returns true if this layout is currently being updated
37171      * @return {Boolean}
37172      */
37173     isUpdating : function(){
37174         return this.updating;
37175     },
37176
37177     /**
37178      * Suspend the LayoutManager from doing auto-layouts while
37179      * making multiple add or remove calls
37180      */
37181     beginUpdate : function(){
37182         this.updating = true;
37183     },
37184
37185     /**
37186      * Restore auto-layouts and optionally disable the manager from performing a layout
37187      * @param {Boolean} noLayout true to disable a layout update
37188      */
37189     endUpdate : function(noLayout){
37190         this.updating = false;
37191         if(!noLayout){
37192             this.layout();
37193         }
37194     },
37195
37196     layout: function(){
37197         // abstract...
37198     },
37199
37200     onRegionResized : function(region, newSize){
37201         this.fireEvent("regionresized", region, newSize);
37202         this.layout();
37203     },
37204
37205     onRegionCollapsed : function(region){
37206         this.fireEvent("regioncollapsed", region);
37207     },
37208
37209     onRegionExpanded : function(region){
37210         this.fireEvent("regionexpanded", region);
37211     },
37212
37213     /**
37214      * Returns the size of the current view. This method normalizes document.body and element embedded layouts and
37215      * performs box-model adjustments.
37216      * @return {Object} The size as an object {width: (the width), height: (the height)}
37217      */
37218     getViewSize : function()
37219     {
37220         var size;
37221         if(this.el.dom != document.body){
37222             size = this.el.getSize();
37223         }else{
37224             size = {width: Roo.lib.Dom.getViewWidth(), height: Roo.lib.Dom.getViewHeight()};
37225         }
37226         size.width -= this.el.getBorderWidth("lr")-this.el.getPadding("lr");
37227         size.height -= this.el.getBorderWidth("tb")-this.el.getPadding("tb");
37228         return size;
37229     },
37230
37231     /**
37232      * Returns the Element this layout is bound to.
37233      * @return {Roo.Element}
37234      */
37235     getEl : function(){
37236         return this.el;
37237     },
37238
37239     /**
37240      * Returns the specified region.
37241      * @param {String} target The region key ('center', 'north', 'south', 'east' or 'west')
37242      * @return {Roo.LayoutRegion}
37243      */
37244     getRegion : function(target){
37245         return this.regions[target.toLowerCase()];
37246     },
37247
37248     onWindowResize : function(){
37249         if(this.monitorWindowResize){
37250             this.layout();
37251         }
37252     }
37253 });
37254 /*
37255  * Based on:
37256  * Ext JS Library 1.1.1
37257  * Copyright(c) 2006-2007, Ext JS, LLC.
37258  *
37259  * Originally Released Under LGPL - original licence link has changed is not relivant.
37260  *
37261  * Fork - LGPL
37262  * <script type="text/javascript">
37263  */
37264 /**
37265  * @class Roo.bootstrap.layout.Border
37266  * @extends Roo.bootstrap.layout.Manager
37267  * This class represents a common layout manager used in desktop applications. For screenshots and more details,
37268  * please see: examples/bootstrap/nested.html<br><br>
37269  
37270 <b>The container the layout is rendered into can be either the body element or any other element.
37271 If it is not the body element, the container needs to either be an absolute positioned element,
37272 or you will need to add "position:relative" to the css of the container.  You will also need to specify
37273 the container size if it is not the body element.</b>
37274
37275 * @constructor
37276 * Create a new Border
37277 * @param {Object} config Configuration options
37278  */
37279 Roo.bootstrap.layout.Border = function(config){
37280     config = config || {};
37281     Roo.bootstrap.layout.Border.superclass.constructor.call(this, config);
37282     
37283     
37284     
37285     Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37286         if(config[region]){
37287             config[region].region = region;
37288             this.addRegion(config[region]);
37289         }
37290     },this);
37291     
37292 };
37293
37294 Roo.bootstrap.layout.Border.regions =  ["north","south","east","west","center"];
37295
37296 Roo.extend(Roo.bootstrap.layout.Border, Roo.bootstrap.layout.Manager, {
37297     
37298     parent : false, // this might point to a 'nest' or a ???
37299     
37300     /**
37301      * Creates and adds a new region if it doesn't already exist.
37302      * @param {String} target The target region key (north, south, east, west or center).
37303      * @param {Object} config The regions config object
37304      * @return {BorderLayoutRegion} The new region
37305      */
37306     addRegion : function(config)
37307     {
37308         if(!this.regions[config.region]){
37309             var r = this.factory(config);
37310             this.bindRegion(r);
37311         }
37312         return this.regions[config.region];
37313     },
37314
37315     // private (kinda)
37316     bindRegion : function(r){
37317         this.regions[r.config.region] = r;
37318         
37319         r.on("visibilitychange",    this.layout, this);
37320         r.on("paneladded",          this.layout, this);
37321         r.on("panelremoved",        this.layout, this);
37322         r.on("invalidated",         this.layout, this);
37323         r.on("resized",             this.onRegionResized, this);
37324         r.on("collapsed",           this.onRegionCollapsed, this);
37325         r.on("expanded",            this.onRegionExpanded, this);
37326     },
37327
37328     /**
37329      * Performs a layout update.
37330      */
37331     layout : function()
37332     {
37333         if(this.updating) {
37334             return;
37335         }
37336         
37337         // render all the rebions if they have not been done alreayd?
37338         Roo.each(Roo.bootstrap.layout.Border.regions, function(region) {
37339             if(this.regions[region] && !this.regions[region].bodyEl){
37340                 this.regions[region].onRender(this.el)
37341             }
37342         },this);
37343         
37344         var size = this.getViewSize();
37345         var w = size.width;
37346         var h = size.height;
37347         var centerW = w;
37348         var centerH = h;
37349         var centerY = 0;
37350         var centerX = 0;
37351         //var x = 0, y = 0;
37352
37353         var rs = this.regions;
37354         var north = rs["north"];
37355         var south = rs["south"]; 
37356         var west = rs["west"];
37357         var east = rs["east"];
37358         var center = rs["center"];
37359         //if(this.hideOnLayout){ // not supported anymore
37360             //c.el.setStyle("display", "none");
37361         //}
37362         if(north && north.isVisible()){
37363             var b = north.getBox();
37364             var m = north.getMargins();
37365             b.width = w - (m.left+m.right);
37366             b.x = m.left;
37367             b.y = m.top;
37368             centerY = b.height + b.y + m.bottom;
37369             centerH -= centerY;
37370             north.updateBox(this.safeBox(b));
37371         }
37372         if(south && south.isVisible()){
37373             var b = south.getBox();
37374             var m = south.getMargins();
37375             b.width = w - (m.left+m.right);
37376             b.x = m.left;
37377             var totalHeight = (b.height + m.top + m.bottom);
37378             b.y = h - totalHeight + m.top;
37379             centerH -= totalHeight;
37380             south.updateBox(this.safeBox(b));
37381         }
37382         if(west && west.isVisible()){
37383             var b = west.getBox();
37384             var m = west.getMargins();
37385             b.height = centerH - (m.top+m.bottom);
37386             b.x = m.left;
37387             b.y = centerY + m.top;
37388             var totalWidth = (b.width + m.left + m.right);
37389             centerX += totalWidth;
37390             centerW -= totalWidth;
37391             west.updateBox(this.safeBox(b));
37392         }
37393         if(east && east.isVisible()){
37394             var b = east.getBox();
37395             var m = east.getMargins();
37396             b.height = centerH - (m.top+m.bottom);
37397             var totalWidth = (b.width + m.left + m.right);
37398             b.x = w - totalWidth + m.left;
37399             b.y = centerY + m.top;
37400             centerW -= totalWidth;
37401             east.updateBox(this.safeBox(b));
37402         }
37403         if(center){
37404             var m = center.getMargins();
37405             var centerBox = {
37406                 x: centerX + m.left,
37407                 y: centerY + m.top,
37408                 width: centerW - (m.left+m.right),
37409                 height: centerH - (m.top+m.bottom)
37410             };
37411             //if(this.hideOnLayout){
37412                 //center.el.setStyle("display", "block");
37413             //}
37414             center.updateBox(this.safeBox(centerBox));
37415         }
37416         this.el.repaint();
37417         this.fireEvent("layout", this);
37418     },
37419
37420     // private
37421     safeBox : function(box){
37422         box.width = Math.max(0, box.width);
37423         box.height = Math.max(0, box.height);
37424         return box;
37425     },
37426
37427     /**
37428      * Adds a ContentPanel (or subclass) to this layout.
37429      * @param {String} target The target region key (north, south, east, west or center).
37430      * @param {Roo.ContentPanel} panel The panel to add
37431      * @return {Roo.ContentPanel} The added panel
37432      */
37433     add : function(target, panel){
37434          
37435         target = target.toLowerCase();
37436         return this.regions[target].add(panel);
37437     },
37438
37439     /**
37440      * Remove a ContentPanel (or subclass) to this layout.
37441      * @param {String} target The target region key (north, south, east, west or center).
37442      * @param {Number/String/Roo.ContentPanel} panel The index, id or panel to remove
37443      * @return {Roo.ContentPanel} The removed panel
37444      */
37445     remove : function(target, panel){
37446         target = target.toLowerCase();
37447         return this.regions[target].remove(panel);
37448     },
37449
37450     /**
37451      * Searches all regions for a panel with the specified id
37452      * @param {String} panelId
37453      * @return {Roo.ContentPanel} The panel or null if it wasn't found
37454      */
37455     findPanel : function(panelId){
37456         var rs = this.regions;
37457         for(var target in rs){
37458             if(typeof rs[target] != "function"){
37459                 var p = rs[target].getPanel(panelId);
37460                 if(p){
37461                     return p;
37462                 }
37463             }
37464         }
37465         return null;
37466     },
37467
37468     /**
37469      * Searches all regions for a panel with the specified id and activates (shows) it.
37470      * @param {String/ContentPanel} panelId The panels id or the panel itself
37471      * @return {Roo.ContentPanel} The shown panel or null
37472      */
37473     showPanel : function(panelId) {
37474       var rs = this.regions;
37475       for(var target in rs){
37476          var r = rs[target];
37477          if(typeof r != "function"){
37478             if(r.hasPanel(panelId)){
37479                return r.showPanel(panelId);
37480             }
37481          }
37482       }
37483       return null;
37484    },
37485
37486    /**
37487      * Restores this layout's state using Roo.state.Manager or the state provided by the passed provider.
37488      * @param {Roo.state.Provider} provider (optional) An alternate state provider
37489      */
37490    /*
37491     restoreState : function(provider){
37492         if(!provider){
37493             provider = Roo.state.Manager;
37494         }
37495         var sm = new Roo.LayoutStateManager();
37496         sm.init(this, provider);
37497     },
37498 */
37499  
37500  
37501     /**
37502      * Adds a xtype elements to the layout.
37503      * <pre><code>
37504
37505 layout.addxtype({
37506        xtype : 'ContentPanel',
37507        region: 'west',
37508        items: [ .... ]
37509    }
37510 );
37511
37512 layout.addxtype({
37513         xtype : 'NestedLayoutPanel',
37514         region: 'west',
37515         layout: {
37516            center: { },
37517            west: { }   
37518         },
37519         items : [ ... list of content panels or nested layout panels.. ]
37520    }
37521 );
37522 </code></pre>
37523      * @param {Object} cfg Xtype definition of item to add.
37524      */
37525     addxtype : function(cfg)
37526     {
37527         // basically accepts a pannel...
37528         // can accept a layout region..!?!?
37529         //Roo.log('Roo.BorderLayout add ' + cfg.xtype)
37530         
37531         
37532         // theory?  children can only be panels??
37533         
37534         //if (!cfg.xtype.match(/Panel$/)) {
37535         //    return false;
37536         //}
37537         var ret = false;
37538         
37539         if (typeof(cfg.region) == 'undefined') {
37540             Roo.log("Failed to add Panel, region was not set");
37541             Roo.log(cfg);
37542             return false;
37543         }
37544         var region = cfg.region;
37545         delete cfg.region;
37546         
37547           
37548         var xitems = [];
37549         if (cfg.items) {
37550             xitems = cfg.items;
37551             delete cfg.items;
37552         }
37553         var nb = false;
37554         
37555         if ( region == 'center') {
37556             Roo.log("Center: " + cfg.title);
37557         }
37558         
37559         
37560         switch(cfg.xtype) 
37561         {
37562             case 'Content':  // ContentPanel (el, cfg)
37563             case 'Scroll':  // ContentPanel (el, cfg)
37564             case 'View': 
37565                 cfg.autoCreate = cfg.autoCreate || true;
37566                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37567                 //} else {
37568                 //    var el = this.el.createChild();
37569                 //    ret = new Roo[cfg.xtype](el, cfg); // new panel!!!!!
37570                 //}
37571                 
37572                 this.add(region, ret);
37573                 break;
37574             
37575             /*
37576             case 'TreePanel': // our new panel!
37577                 cfg.el = this.el.createChild();
37578                 ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37579                 this.add(region, ret);
37580                 break;
37581             */
37582             
37583             case 'Nest': 
37584                 // create a new Layout (which is  a Border Layout...
37585                 
37586                 var clayout = cfg.layout;
37587                 clayout.el  = this.el.createChild();
37588                 clayout.items   = clayout.items  || [];
37589                 
37590                 delete cfg.layout;
37591                 
37592                 // replace this exitems with the clayout ones..
37593                 xitems = clayout.items;
37594                  
37595                 // force background off if it's in center...
37596                 if (region == 'center' && this.active && this.getRegion('center').panels.length < 1) {
37597                     cfg.background = false;
37598                 }
37599                 cfg.layout  = new Roo.bootstrap.layout.Border(clayout);
37600                 
37601                 
37602                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37603                 //console.log('adding nested layout panel '  + cfg.toSource());
37604                 this.add(region, ret);
37605                 nb = {}; /// find first...
37606                 break;
37607             
37608             case 'Grid':
37609                 
37610                 // needs grid and region
37611                 
37612                 //var el = this.getRegion(region).el.createChild();
37613                 /*
37614                  *var el = this.el.createChild();
37615                 // create the grid first...
37616                 cfg.grid.container = el;
37617                 cfg.grid = new cfg.grid.xns[cfg.grid.xtype](cfg.grid);
37618                 */
37619                 
37620                 if (region == 'center' && this.active ) {
37621                     cfg.background = false;
37622                 }
37623                 
37624                 ret = new cfg.xns[cfg.xtype](cfg); // new panel!!!!!
37625                 
37626                 this.add(region, ret);
37627                 /*
37628                 if (cfg.background) {
37629                     // render grid on panel activation (if panel background)
37630                     ret.on('activate', function(gp) {
37631                         if (!gp.grid.rendered) {
37632                     //        gp.grid.render(el);
37633                         }
37634                     });
37635                 } else {
37636                   //  cfg.grid.render(el);
37637                 }
37638                 */
37639                 break;
37640            
37641            
37642             case 'Border': // it can get called on it'self... - might need to check if this is fixed?
37643                 // it was the old xcomponent building that caused this before.
37644                 // espeically if border is the top element in the tree.
37645                 ret = this;
37646                 break; 
37647                 
37648                     
37649                 
37650                 
37651                 
37652             default:
37653                 /*
37654                 if (typeof(Roo[cfg.xtype]) != 'undefined') {
37655                     
37656                     ret = new Roo[cfg.xtype](cfg); // new panel!!!!!
37657                     this.add(region, ret);
37658                 } else {
37659                 */
37660                     Roo.log(cfg);
37661                     throw "Can not add '" + cfg.xtype + "' to Border";
37662                     return null;
37663              
37664                                 
37665              
37666         }
37667         this.beginUpdate();
37668         // add children..
37669         var region = '';
37670         var abn = {};
37671         Roo.each(xitems, function(i)  {
37672             region = nb && i.region ? i.region : false;
37673             
37674             var add = ret.addxtype(i);
37675            
37676             if (region) {
37677                 nb[region] = nb[region] == undefined ? 0 : nb[region]+1;
37678                 if (!i.background) {
37679                     abn[region] = nb[region] ;
37680                 }
37681             }
37682             
37683         });
37684         this.endUpdate();
37685
37686         // make the last non-background panel active..
37687         //if (nb) { Roo.log(abn); }
37688         if (nb) {
37689             
37690             for(var r in abn) {
37691                 region = this.getRegion(r);
37692                 if (region) {
37693                     // tried using nb[r], but it does not work..
37694                      
37695                     region.showPanel(abn[r]);
37696                    
37697                 }
37698             }
37699         }
37700         return ret;
37701         
37702     },
37703     
37704     
37705 // private
37706     factory : function(cfg)
37707     {
37708         
37709         var validRegions = Roo.bootstrap.layout.Border.regions;
37710
37711         var target = cfg.region;
37712         cfg.mgr = this;
37713         
37714         var r = Roo.bootstrap.layout;
37715         Roo.log(target);
37716         switch(target){
37717             case "north":
37718                 return new r.North(cfg);
37719             case "south":
37720                 return new r.South(cfg);
37721             case "east":
37722                 return new r.East(cfg);
37723             case "west":
37724                 return new r.West(cfg);
37725             case "center":
37726                 return new r.Center(cfg);
37727         }
37728         throw 'Layout region "'+target+'" not supported.';
37729     }
37730     
37731     
37732 });
37733  /*
37734  * Based on:
37735  * Ext JS Library 1.1.1
37736  * Copyright(c) 2006-2007, Ext JS, LLC.
37737  *
37738  * Originally Released Under LGPL - original licence link has changed is not relivant.
37739  *
37740  * Fork - LGPL
37741  * <script type="text/javascript">
37742  */
37743  
37744 /**
37745  * @class Roo.bootstrap.layout.Basic
37746  * @extends Roo.util.Observable
37747  * This class represents a lightweight region in a layout manager. This region does not move dom nodes
37748  * and does not have a titlebar, tabs or any other features. All it does is size and position 
37749  * panels. To create a BasicLayoutRegion, add lightweight:true or basic:true to your regions config.
37750  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
37751  * @cfg {string}   region  the region that it inhabits..
37752  * @cfg {bool}   skipConfig skip config?
37753  * 
37754
37755  */
37756 Roo.bootstrap.layout.Basic = function(config){
37757     
37758     this.mgr = config.mgr;
37759     
37760     this.position = config.region;
37761     
37762     var skipConfig = config.skipConfig;
37763     
37764     this.events = {
37765         /**
37766          * @scope Roo.BasicLayoutRegion
37767          */
37768         
37769         /**
37770          * @event beforeremove
37771          * Fires before a panel is removed (or closed). To cancel the removal set "e.cancel = true" on the event argument.
37772          * @param {Roo.LayoutRegion} this
37773          * @param {Roo.ContentPanel} panel The panel
37774          * @param {Object} e The cancel event object
37775          */
37776         "beforeremove" : true,
37777         /**
37778          * @event invalidated
37779          * Fires when the layout for this region is changed.
37780          * @param {Roo.LayoutRegion} this
37781          */
37782         "invalidated" : true,
37783         /**
37784          * @event visibilitychange
37785          * Fires when this region is shown or hidden 
37786          * @param {Roo.LayoutRegion} this
37787          * @param {Boolean} visibility true or false
37788          */
37789         "visibilitychange" : true,
37790         /**
37791          * @event paneladded
37792          * Fires when a panel is added. 
37793          * @param {Roo.LayoutRegion} this
37794          * @param {Roo.ContentPanel} panel The panel
37795          */
37796         "paneladded" : true,
37797         /**
37798          * @event panelremoved
37799          * Fires when a panel is removed. 
37800          * @param {Roo.LayoutRegion} this
37801          * @param {Roo.ContentPanel} panel The panel
37802          */
37803         "panelremoved" : true,
37804         /**
37805          * @event beforecollapse
37806          * Fires when this region before collapse.
37807          * @param {Roo.LayoutRegion} this
37808          */
37809         "beforecollapse" : true,
37810         /**
37811          * @event collapsed
37812          * Fires when this region is collapsed.
37813          * @param {Roo.LayoutRegion} this
37814          */
37815         "collapsed" : true,
37816         /**
37817          * @event expanded
37818          * Fires when this region is expanded.
37819          * @param {Roo.LayoutRegion} this
37820          */
37821         "expanded" : true,
37822         /**
37823          * @event slideshow
37824          * Fires when this region is slid into view.
37825          * @param {Roo.LayoutRegion} this
37826          */
37827         "slideshow" : true,
37828         /**
37829          * @event slidehide
37830          * Fires when this region slides out of view. 
37831          * @param {Roo.LayoutRegion} this
37832          */
37833         "slidehide" : true,
37834         /**
37835          * @event panelactivated
37836          * Fires when a panel is activated. 
37837          * @param {Roo.LayoutRegion} this
37838          * @param {Roo.ContentPanel} panel The activated panel
37839          */
37840         "panelactivated" : true,
37841         /**
37842          * @event resized
37843          * Fires when the user resizes this region. 
37844          * @param {Roo.LayoutRegion} this
37845          * @param {Number} newSize The new size (width for east/west, height for north/south)
37846          */
37847         "resized" : true
37848     };
37849     /** A collection of panels in this region. @type Roo.util.MixedCollection */
37850     this.panels = new Roo.util.MixedCollection();
37851     this.panels.getKey = this.getPanelId.createDelegate(this);
37852     this.box = null;
37853     this.activePanel = null;
37854     // ensure listeners are added...
37855     
37856     if (config.listeners || config.events) {
37857         Roo.bootstrap.layout.Basic.superclass.constructor.call(this, {
37858             listeners : config.listeners || {},
37859             events : config.events || {}
37860         });
37861     }
37862     
37863     if(skipConfig !== true){
37864         this.applyConfig(config);
37865     }
37866 };
37867
37868 Roo.extend(Roo.bootstrap.layout.Basic, Roo.util.Observable,
37869 {
37870     getPanelId : function(p){
37871         return p.getId();
37872     },
37873     
37874     applyConfig : function(config){
37875         this.margins = config.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
37876         this.config = config;
37877         
37878     },
37879     
37880     /**
37881      * Resizes the region to the specified size. For vertical regions (west, east) this adjusts 
37882      * the width, for horizontal (north, south) the height.
37883      * @param {Number} newSize The new width or height
37884      */
37885     resizeTo : function(newSize){
37886         var el = this.el ? this.el :
37887                  (this.activePanel ? this.activePanel.getEl() : null);
37888         if(el){
37889             switch(this.position){
37890                 case "east":
37891                 case "west":
37892                     el.setWidth(newSize);
37893                     this.fireEvent("resized", this, newSize);
37894                 break;
37895                 case "north":
37896                 case "south":
37897                     el.setHeight(newSize);
37898                     this.fireEvent("resized", this, newSize);
37899                 break;                
37900             }
37901         }
37902     },
37903     
37904     getBox : function(){
37905         return this.activePanel ? this.activePanel.getEl().getBox(false, true) : null;
37906     },
37907     
37908     getMargins : function(){
37909         return this.margins;
37910     },
37911     
37912     updateBox : function(box){
37913         this.box = box;
37914         var el = this.activePanel.getEl();
37915         el.dom.style.left = box.x + "px";
37916         el.dom.style.top = box.y + "px";
37917         this.activePanel.setSize(box.width, box.height);
37918     },
37919     
37920     /**
37921      * Returns the container element for this region.
37922      * @return {Roo.Element}
37923      */
37924     getEl : function(){
37925         return this.activePanel;
37926     },
37927     
37928     /**
37929      * Returns true if this region is currently visible.
37930      * @return {Boolean}
37931      */
37932     isVisible : function(){
37933         return this.activePanel ? true : false;
37934     },
37935     
37936     setActivePanel : function(panel){
37937         panel = this.getPanel(panel);
37938         if(this.activePanel && this.activePanel != panel){
37939             this.activePanel.setActiveState(false);
37940             this.activePanel.getEl().setLeftTop(-10000,-10000);
37941         }
37942         this.activePanel = panel;
37943         panel.setActiveState(true);
37944         if(this.box){
37945             panel.setSize(this.box.width, this.box.height);
37946         }
37947         this.fireEvent("panelactivated", this, panel);
37948         this.fireEvent("invalidated");
37949     },
37950     
37951     /**
37952      * Show the specified panel.
37953      * @param {Number/String/ContentPanel} panelId The panels index, id or the panel itself
37954      * @return {Roo.ContentPanel} The shown panel or null
37955      */
37956     showPanel : function(panel){
37957         panel = this.getPanel(panel);
37958         if(panel){
37959             this.setActivePanel(panel);
37960         }
37961         return panel;
37962     },
37963     
37964     /**
37965      * Get the active panel for this region.
37966      * @return {Roo.ContentPanel} The active panel or null
37967      */
37968     getActivePanel : function(){
37969         return this.activePanel;
37970     },
37971     
37972     /**
37973      * Add the passed ContentPanel(s)
37974      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
37975      * @return {Roo.ContentPanel} The panel added (if only one was added)
37976      */
37977     add : function(panel){
37978         if(arguments.length > 1){
37979             for(var i = 0, len = arguments.length; i < len; i++) {
37980                 this.add(arguments[i]);
37981             }
37982             return null;
37983         }
37984         if(this.hasPanel(panel)){
37985             this.showPanel(panel);
37986             return panel;
37987         }
37988         var el = panel.getEl();
37989         if(el.dom.parentNode != this.mgr.el.dom){
37990             this.mgr.el.dom.appendChild(el.dom);
37991         }
37992         if(panel.setRegion){
37993             panel.setRegion(this);
37994         }
37995         this.panels.add(panel);
37996         el.setStyle("position", "absolute");
37997         if(!panel.background){
37998             this.setActivePanel(panel);
37999             if(this.config.initialSize && this.panels.getCount()==1){
38000                 this.resizeTo(this.config.initialSize);
38001             }
38002         }
38003         this.fireEvent("paneladded", this, panel);
38004         return panel;
38005     },
38006     
38007     /**
38008      * Returns true if the panel is in this region.
38009      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38010      * @return {Boolean}
38011      */
38012     hasPanel : function(panel){
38013         if(typeof panel == "object"){ // must be panel obj
38014             panel = panel.getId();
38015         }
38016         return this.getPanel(panel) ? true : false;
38017     },
38018     
38019     /**
38020      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38021      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38022      * @param {Boolean} preservePanel Overrides the config preservePanel option
38023      * @return {Roo.ContentPanel} The panel that was removed
38024      */
38025     remove : function(panel, preservePanel){
38026         panel = this.getPanel(panel);
38027         if(!panel){
38028             return null;
38029         }
38030         var e = {};
38031         this.fireEvent("beforeremove", this, panel, e);
38032         if(e.cancel === true){
38033             return null;
38034         }
38035         var panelId = panel.getId();
38036         this.panels.removeKey(panelId);
38037         return panel;
38038     },
38039     
38040     /**
38041      * Returns the panel specified or null if it's not in this region.
38042      * @param {Number/String/ContentPanel} panel The panels index, id or the panel itself
38043      * @return {Roo.ContentPanel}
38044      */
38045     getPanel : function(id){
38046         if(typeof id == "object"){ // must be panel obj
38047             return id;
38048         }
38049         return this.panels.get(id);
38050     },
38051     
38052     /**
38053      * Returns this regions position (north/south/east/west/center).
38054      * @return {String} 
38055      */
38056     getPosition: function(){
38057         return this.position;    
38058     }
38059 });/*
38060  * Based on:
38061  * Ext JS Library 1.1.1
38062  * Copyright(c) 2006-2007, Ext JS, LLC.
38063  *
38064  * Originally Released Under LGPL - original licence link has changed is not relivant.
38065  *
38066  * Fork - LGPL
38067  * <script type="text/javascript">
38068  */
38069  
38070 /**
38071  * @class Roo.bootstrap.layout.Region
38072  * @extends Roo.bootstrap.layout.Basic
38073  * This class represents a region in a layout manager.
38074  
38075  * @cfg {Object}    margins         Margins for the element (defaults to {top: 0, left: 0, right:0, bottom: 0})
38076  * @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})
38077  * @cfg {String}    tabPosition     (top|bottom) "top" or "bottom" (defaults to "bottom")
38078  * @cfg {Boolean}   alwaysShowTabs  True to always display tabs even when there is only 1 panel (defaults to false)
38079  * @cfg {Boolean}   autoScroll      True to enable overflow scrolling (defaults to false)
38080  * @cfg {Boolean}   titlebar        True to display a title bar (defaults to true)
38081  * @cfg {String}    title           The title for the region (overrides panel titles)
38082  * @cfg {Boolean}   animate         True to animate expand/collapse (defaults to false)
38083  * @cfg {Boolean}   autoHide        False to disable auto hiding when the mouse leaves the "floated" region (defaults to true)
38084  * @cfg {Boolean}   preservePanels  True to preserve removed panels so they can be readded later (defaults to false)
38085  * @cfg {Boolean}   closeOnTab      True to place the close icon on the tabs instead of the region titlebar (defaults to false)
38086  * @cfg {Boolean}   hideTabs        True to hide the tab strip (defaults to false)
38087  * @cfg {Boolean}   resizeTabs      True to enable automatic tab resizing. This will resize the tabs so they are all the same size and fit within
38088  *                      the space available, similar to FireFox 1.5 tabs (defaults to false)
38089  * @cfg {Number}    minTabWidth     The minimum tab width (defaults to 40)
38090  * @cfg {Number}    preferredTabWidth The preferred tab width (defaults to 150)
38091  * @cfg {String}    overflow       (hidden|visible) if you have menus in the region, then you need to set this to visible.
38092
38093  * @cfg {Boolean}   hidden          True to start the region hidden (defaults to false)
38094  * @cfg {Boolean}   hideWhenEmpty   True to hide the region when it has no panels
38095  * @cfg {Boolean}   disableTabTips  True to disable tab tooltips
38096  * @cfg {Number}    width           For East/West panels
38097  * @cfg {Number}    height          For North/South panels
38098  * @cfg {Boolean}   split           To show the splitter
38099  * @cfg {Boolean}   toolbar         xtype configuration for a toolbar - shows on right of tabbar
38100  * 
38101  * @cfg {string}   cls             Extra CSS classes to add to region
38102  * 
38103  * @cfg {Roo.bootstrap.layout.Manager}   mgr The manager
38104  * @cfg {string}   region  the region that it inhabits..
38105  *
38106
38107  * @xxxcfg {Boolean}   collapsible     DISABLED False to disable collapsing (defaults to true)
38108  * @xxxcfg {Boolean}   collapsed       DISABLED True to set the initial display to collapsed (defaults to false)
38109
38110  * @xxxcfg {String}    collapsedTitle  DISABLED Optional string message to display in the collapsed block of a north or south region
38111  * @xxxxcfg {Boolean}   floatable       DISABLED False to disable floating (defaults to true)
38112  * @xxxxcfg {Boolean}   showPin         True to show a pin button NOT SUPPORTED YET
38113  */
38114 Roo.bootstrap.layout.Region = function(config)
38115 {
38116     this.applyConfig(config);
38117
38118     var mgr = config.mgr;
38119     var pos = config.region;
38120     config.skipConfig = true;
38121     Roo.bootstrap.layout.Region.superclass.constructor.call(this, config);
38122     
38123     if (mgr.el) {
38124         this.onRender(mgr.el);   
38125     }
38126      
38127     this.visible = true;
38128     this.collapsed = false;
38129     this.unrendered_panels = [];
38130 };
38131
38132 Roo.extend(Roo.bootstrap.layout.Region, Roo.bootstrap.layout.Basic, {
38133
38134     position: '', // set by wrapper (eg. north/south etc..)
38135     unrendered_panels : null,  // unrendered panels.
38136     
38137     tabPosition : false,
38138     
38139     mgr: false, // points to 'Border'
38140     
38141     
38142     createBody : function(){
38143         /** This region's body element 
38144         * @type Roo.Element */
38145         this.bodyEl = this.el.createChild({
38146                 tag: "div",
38147                 cls: "roo-layout-panel-body tab-content" // bootstrap added...
38148         });
38149     },
38150
38151     onRender: function(ctr, pos)
38152     {
38153         var dh = Roo.DomHelper;
38154         /** This region's container element 
38155         * @type Roo.Element */
38156         this.el = dh.append(ctr.dom, {
38157                 tag: "div",
38158                 cls: (this.config.cls || '') + " roo-layout-region roo-layout-panel roo-layout-panel-" + this.position
38159             }, true);
38160         /** This region's title element 
38161         * @type Roo.Element */
38162     
38163         this.titleEl = dh.append(this.el.dom,  {
38164                 tag: "div",
38165                 unselectable: "on",
38166                 cls: "roo-unselectable roo-layout-panel-hd breadcrumb roo-layout-title-" + this.position,
38167                 children:[
38168                     {tag: "span", cls: "roo-unselectable roo-layout-panel-hd-text", unselectable: "on", html: "&#160;"},
38169                     {tag: "div", cls: "roo-unselectable roo-layout-panel-hd-tools", unselectable: "on"}
38170                 ]
38171             }, true);
38172         
38173         this.titleEl.enableDisplayMode();
38174         /** This region's title text element 
38175         * @type HTMLElement */
38176         this.titleTextEl = this.titleEl.dom.firstChild;
38177         this.tools = Roo.get(this.titleEl.dom.childNodes[1], true);
38178         /*
38179         this.closeBtn = this.createTool(this.tools.dom, "roo-layout-close");
38180         this.closeBtn.enableDisplayMode();
38181         this.closeBtn.on("click", this.closeClicked, this);
38182         this.closeBtn.hide();
38183     */
38184         this.createBody(this.config);
38185         if(this.config.hideWhenEmpty){
38186             this.hide();
38187             this.on("paneladded", this.validateVisibility, this);
38188             this.on("panelremoved", this.validateVisibility, this);
38189         }
38190         if(this.autoScroll){
38191             this.bodyEl.setStyle("overflow", "auto");
38192         }else{
38193             this.bodyEl.setStyle("overflow", this.config.overflow || 'hidden');
38194         }
38195         //if(c.titlebar !== false){
38196             if((!this.config.titlebar && !this.config.title) || this.config.titlebar === false){
38197                 this.titleEl.hide();
38198             }else{
38199                 this.titleEl.show();
38200                 if(this.config.title){
38201                     this.titleTextEl.innerHTML = this.config.title;
38202                 }
38203             }
38204         //}
38205         if(this.config.collapsed){
38206             this.collapse(true);
38207         }
38208         if(this.config.hidden){
38209             this.hide();
38210         }
38211         
38212         if (this.unrendered_panels && this.unrendered_panels.length) {
38213             for (var i =0;i< this.unrendered_panels.length; i++) {
38214                 this.add(this.unrendered_panels[i]);
38215             }
38216             this.unrendered_panels = null;
38217             
38218         }
38219         
38220     },
38221     
38222     applyConfig : function(c)
38223     {
38224         /*
38225          *if(c.collapsible && this.position != "center" && !this.collapsedEl){
38226             var dh = Roo.DomHelper;
38227             if(c.titlebar !== false){
38228                 this.collapseBtn = this.createTool(this.tools.dom, "roo-layout-collapse-"+this.position);
38229                 this.collapseBtn.on("click", this.collapse, this);
38230                 this.collapseBtn.enableDisplayMode();
38231                 /*
38232                 if(c.showPin === true || this.showPin){
38233                     this.stickBtn = this.createTool(this.tools.dom, "roo-layout-stick");
38234                     this.stickBtn.enableDisplayMode();
38235                     this.stickBtn.on("click", this.expand, this);
38236                     this.stickBtn.hide();
38237                 }
38238                 
38239             }
38240             */
38241             /** This region's collapsed element
38242             * @type Roo.Element */
38243             /*
38244              *
38245             this.collapsedEl = dh.append(this.mgr.el.dom, {cls: "x-layout-collapsed x-layout-collapsed-"+this.position, children:[
38246                 {cls: "x-layout-collapsed-tools", children:[{cls: "x-layout-ctools-inner"}]}
38247             ]}, true);
38248             
38249             if(c.floatable !== false){
38250                this.collapsedEl.addClassOnOver("x-layout-collapsed-over");
38251                this.collapsedEl.on("click", this.collapseClick, this);
38252             }
38253
38254             if(c.collapsedTitle && (this.position == "north" || this.position== "south")) {
38255                 this.collapsedTitleTextEl = dh.append(this.collapsedEl.dom, {tag: "div", cls: "x-unselectable x-layout-panel-hd-text",
38256                    id: "message", unselectable: "on", style:{"float":"left"}});
38257                this.collapsedTitleTextEl.innerHTML = c.collapsedTitle;
38258              }
38259             this.expandBtn = this.createTool(this.collapsedEl.dom.firstChild.firstChild, "x-layout-expand-"+this.position);
38260             this.expandBtn.on("click", this.expand, this);
38261             
38262         }
38263         
38264         if(this.collapseBtn){
38265             this.collapseBtn.setVisible(c.collapsible == true);
38266         }
38267         
38268         this.cmargins = c.cmargins || this.cmargins ||
38269                          (this.position == "west" || this.position == "east" ?
38270                              {top: 0, left: 2, right:2, bottom: 0} :
38271                              {top: 2, left: 0, right:0, bottom: 2});
38272         */
38273         this.margins = c.margins || this.margins || {top: 0, left: 0, right:0, bottom: 0};
38274         
38275         
38276         this.tabPosition = [ 'top','bottom', 'west'].indexOf(c.tabPosition) > -1 ? c.tabPosition : "top";
38277         
38278         this.autoScroll = c.autoScroll || false;
38279         
38280         
38281        
38282         
38283         this.duration = c.duration || .30;
38284         this.slideDuration = c.slideDuration || .45;
38285         this.config = c;
38286        
38287     },
38288     /**
38289      * Returns true if this region is currently visible.
38290      * @return {Boolean}
38291      */
38292     isVisible : function(){
38293         return this.visible;
38294     },
38295
38296     /**
38297      * Updates the title for collapsed north/south regions (used with {@link #collapsedTitle} config option)
38298      * @param {String} title (optional) The title text (accepts HTML markup, defaults to the numeric character reference for a non-breaking space, "&amp;#160;")
38299      */
38300     //setCollapsedTitle : function(title){
38301     //    title = title || "&#160;";
38302      //   if(this.collapsedTitleTextEl){
38303       //      this.collapsedTitleTextEl.innerHTML = title;
38304        // }
38305     //},
38306
38307     getBox : function(){
38308         var b;
38309       //  if(!this.collapsed){
38310             b = this.el.getBox(false, true);
38311        // }else{
38312           //  b = this.collapsedEl.getBox(false, true);
38313         //}
38314         return b;
38315     },
38316
38317     getMargins : function(){
38318         return this.margins;
38319         //return this.collapsed ? this.cmargins : this.margins;
38320     },
38321 /*
38322     highlight : function(){
38323         this.el.addClass("x-layout-panel-dragover");
38324     },
38325
38326     unhighlight : function(){
38327         this.el.removeClass("x-layout-panel-dragover");
38328     },
38329 */
38330     updateBox : function(box)
38331     {
38332         if (!this.bodyEl) {
38333             return; // not rendered yet..
38334         }
38335         
38336         this.box = box;
38337         if(!this.collapsed){
38338             this.el.dom.style.left = box.x + "px";
38339             this.el.dom.style.top = box.y + "px";
38340             this.updateBody(box.width, box.height);
38341         }else{
38342             this.collapsedEl.dom.style.left = box.x + "px";
38343             this.collapsedEl.dom.style.top = box.y + "px";
38344             this.collapsedEl.setSize(box.width, box.height);
38345         }
38346         if(this.tabs){
38347             this.tabs.autoSizeTabs();
38348         }
38349     },
38350
38351     updateBody : function(w, h)
38352     {
38353         if(w !== null){
38354             this.el.setWidth(w);
38355             w -= this.el.getBorderWidth("rl");
38356             if(this.config.adjustments){
38357                 w += this.config.adjustments[0];
38358             }
38359         }
38360         if(h !== null && h > 0){
38361             this.el.setHeight(h);
38362             h = this.titleEl && this.titleEl.isDisplayed() ? h - (this.titleEl.getHeight()||0) : h;
38363             h -= this.el.getBorderWidth("tb");
38364             if(this.config.adjustments){
38365                 h += this.config.adjustments[1];
38366             }
38367             this.bodyEl.setHeight(h);
38368             if(this.tabs){
38369                 h = this.tabs.syncHeight(h);
38370             }
38371         }
38372         if(this.panelSize){
38373             w = w !== null ? w : this.panelSize.width;
38374             h = h !== null ? h : this.panelSize.height;
38375         }
38376         if(this.activePanel){
38377             var el = this.activePanel.getEl();
38378             w = w !== null ? w : el.getWidth();
38379             h = h !== null ? h : el.getHeight();
38380             this.panelSize = {width: w, height: h};
38381             this.activePanel.setSize(w, h);
38382         }
38383         if(Roo.isIE && this.tabs){
38384             this.tabs.el.repaint();
38385         }
38386     },
38387
38388     /**
38389      * Returns the container element for this region.
38390      * @return {Roo.Element}
38391      */
38392     getEl : function(){
38393         return this.el;
38394     },
38395
38396     /**
38397      * Hides this region.
38398      */
38399     hide : function(){
38400         //if(!this.collapsed){
38401             this.el.dom.style.left = "-2000px";
38402             this.el.hide();
38403         //}else{
38404          //   this.collapsedEl.dom.style.left = "-2000px";
38405          //   this.collapsedEl.hide();
38406        // }
38407         this.visible = false;
38408         this.fireEvent("visibilitychange", this, false);
38409     },
38410
38411     /**
38412      * Shows this region if it was previously hidden.
38413      */
38414     show : function(){
38415         //if(!this.collapsed){
38416             this.el.show();
38417         //}else{
38418         //    this.collapsedEl.show();
38419        // }
38420         this.visible = true;
38421         this.fireEvent("visibilitychange", this, true);
38422     },
38423 /*
38424     closeClicked : function(){
38425         if(this.activePanel){
38426             this.remove(this.activePanel);
38427         }
38428     },
38429
38430     collapseClick : function(e){
38431         if(this.isSlid){
38432            e.stopPropagation();
38433            this.slideIn();
38434         }else{
38435            e.stopPropagation();
38436            this.slideOut();
38437         }
38438     },
38439 */
38440     /**
38441      * Collapses this region.
38442      * @param {Boolean} skipAnim (optional) true to collapse the element without animation (if animate is true)
38443      */
38444     /*
38445     collapse : function(skipAnim, skipCheck = false){
38446         if(this.collapsed) {
38447             return;
38448         }
38449         
38450         if(skipCheck || this.fireEvent("beforecollapse", this) != false){
38451             
38452             this.collapsed = true;
38453             if(this.split){
38454                 this.split.el.hide();
38455             }
38456             if(this.config.animate && skipAnim !== true){
38457                 this.fireEvent("invalidated", this);
38458                 this.animateCollapse();
38459             }else{
38460                 this.el.setLocation(-20000,-20000);
38461                 this.el.hide();
38462                 this.collapsedEl.show();
38463                 this.fireEvent("collapsed", this);
38464                 this.fireEvent("invalidated", this);
38465             }
38466         }
38467         
38468     },
38469 */
38470     animateCollapse : function(){
38471         // overridden
38472     },
38473
38474     /**
38475      * Expands this region if it was previously collapsed.
38476      * @param {Roo.EventObject} e The event that triggered the expand (or null if calling manually)
38477      * @param {Boolean} skipAnim (optional) true to expand the element without animation (if animate is true)
38478      */
38479     /*
38480     expand : function(e, skipAnim){
38481         if(e) {
38482             e.stopPropagation();
38483         }
38484         if(!this.collapsed || this.el.hasActiveFx()) {
38485             return;
38486         }
38487         if(this.isSlid){
38488             this.afterSlideIn();
38489             skipAnim = true;
38490         }
38491         this.collapsed = false;
38492         if(this.config.animate && skipAnim !== true){
38493             this.animateExpand();
38494         }else{
38495             this.el.show();
38496             if(this.split){
38497                 this.split.el.show();
38498             }
38499             this.collapsedEl.setLocation(-2000,-2000);
38500             this.collapsedEl.hide();
38501             this.fireEvent("invalidated", this);
38502             this.fireEvent("expanded", this);
38503         }
38504     },
38505 */
38506     animateExpand : function(){
38507         // overridden
38508     },
38509
38510     initTabs : function()
38511     {
38512         //this.bodyEl.setStyle("overflow", "hidden"); -- this is set in render?
38513         
38514         var ts = new Roo.bootstrap.panel.Tabs({
38515             el: this.bodyEl.dom,
38516             region : this,
38517             tabPosition: this.tabPosition ? this.tabPosition  : 'top',
38518             disableTooltips: this.config.disableTabTips,
38519             toolbar : this.config.toolbar
38520         });
38521         
38522         if(this.config.hideTabs){
38523             ts.stripWrap.setDisplayed(false);
38524         }
38525         this.tabs = ts;
38526         ts.resizeTabs = this.config.resizeTabs === true;
38527         ts.minTabWidth = this.config.minTabWidth || 40;
38528         ts.maxTabWidth = this.config.maxTabWidth || 250;
38529         ts.preferredTabWidth = this.config.preferredTabWidth || 150;
38530         ts.monitorResize = false;
38531         //ts.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden"); // this is set in render?
38532         ts.bodyEl.addClass('roo-layout-tabs-body');
38533         this.panels.each(this.initPanelAsTab, this);
38534     },
38535
38536     initPanelAsTab : function(panel){
38537         var ti = this.tabs.addTab(
38538             panel.getEl().id,
38539             panel.getTitle(),
38540             null,
38541             this.config.closeOnTab && panel.isClosable(),
38542             panel.tpl
38543         );
38544         if(panel.tabTip !== undefined){
38545             ti.setTooltip(panel.tabTip);
38546         }
38547         ti.on("activate", function(){
38548               this.setActivePanel(panel);
38549         }, this);
38550         
38551         if(this.config.closeOnTab){
38552             ti.on("beforeclose", function(t, e){
38553                 e.cancel = true;
38554                 this.remove(panel);
38555             }, this);
38556         }
38557         
38558         panel.tabItem = ti;
38559         
38560         return ti;
38561     },
38562
38563     updatePanelTitle : function(panel, title)
38564     {
38565         if(this.activePanel == panel){
38566             this.updateTitle(title);
38567         }
38568         if(this.tabs){
38569             var ti = this.tabs.getTab(panel.getEl().id);
38570             ti.setText(title);
38571             if(panel.tabTip !== undefined){
38572                 ti.setTooltip(panel.tabTip);
38573             }
38574         }
38575     },
38576
38577     updateTitle : function(title){
38578         if(this.titleTextEl && !this.config.title){
38579             this.titleTextEl.innerHTML = (typeof title != "undefined" && title.length > 0 ? title : "&#160;");
38580         }
38581     },
38582
38583     setActivePanel : function(panel)
38584     {
38585         panel = this.getPanel(panel);
38586         if(this.activePanel && this.activePanel != panel){
38587             if(this.activePanel.setActiveState(false) === false){
38588                 return;
38589             }
38590         }
38591         this.activePanel = panel;
38592         panel.setActiveState(true);
38593         if(this.panelSize){
38594             panel.setSize(this.panelSize.width, this.panelSize.height);
38595         }
38596         if(this.closeBtn){
38597             this.closeBtn.setVisible(!this.config.closeOnTab && !this.isSlid && panel.isClosable());
38598         }
38599         this.updateTitle(panel.getTitle());
38600         if(this.tabs){
38601             this.fireEvent("invalidated", this);
38602         }
38603         this.fireEvent("panelactivated", this, panel);
38604     },
38605
38606     /**
38607      * Shows the specified panel.
38608      * @param {Number/String/ContentPanel} panelId The panel's index, id or the panel itself
38609      * @return {Roo.ContentPanel} The shown panel, or null if a panel could not be found from panelId
38610      */
38611     showPanel : function(panel)
38612     {
38613         panel = this.getPanel(panel);
38614         if(panel){
38615             if(this.tabs){
38616                 var tab = this.tabs.getTab(panel.getEl().id);
38617                 if(tab.isHidden()){
38618                     this.tabs.unhideTab(tab.id);
38619                 }
38620                 tab.activate();
38621             }else{
38622                 this.setActivePanel(panel);
38623             }
38624         }
38625         return panel;
38626     },
38627
38628     /**
38629      * Get the active panel for this region.
38630      * @return {Roo.ContentPanel} The active panel or null
38631      */
38632     getActivePanel : function(){
38633         return this.activePanel;
38634     },
38635
38636     validateVisibility : function(){
38637         if(this.panels.getCount() < 1){
38638             this.updateTitle("&#160;");
38639             this.closeBtn.hide();
38640             this.hide();
38641         }else{
38642             if(!this.isVisible()){
38643                 this.show();
38644             }
38645         }
38646     },
38647
38648     /**
38649      * Adds the passed ContentPanel(s) to this region.
38650      * @param {ContentPanel...} panel The ContentPanel(s) to add (you can pass more than one)
38651      * @return {Roo.ContentPanel} The panel added (if only one was added; null otherwise)
38652      */
38653     add : function(panel)
38654     {
38655         if(arguments.length > 1){
38656             for(var i = 0, len = arguments.length; i < len; i++) {
38657                 this.add(arguments[i]);
38658             }
38659             return null;
38660         }
38661         
38662         // if we have not been rendered yet, then we can not really do much of this..
38663         if (!this.bodyEl) {
38664             this.unrendered_panels.push(panel);
38665             return panel;
38666         }
38667         
38668         
38669         
38670         
38671         if(this.hasPanel(panel)){
38672             this.showPanel(panel);
38673             return panel;
38674         }
38675         panel.setRegion(this);
38676         this.panels.add(panel);
38677        /* if(this.panels.getCount() == 1 && !this.config.alwaysShowTabs){
38678             // sinle panel - no tab...?? would it not be better to render it with the tabs,
38679             // and hide them... ???
38680             this.bodyEl.dom.appendChild(panel.getEl().dom);
38681             if(panel.background !== true){
38682                 this.setActivePanel(panel);
38683             }
38684             this.fireEvent("paneladded", this, panel);
38685             return panel;
38686         }
38687         */
38688         if(!this.tabs){
38689             this.initTabs();
38690         }else{
38691             this.initPanelAsTab(panel);
38692         }
38693         
38694         
38695         if(panel.background !== true){
38696             this.tabs.activate(panel.getEl().id);
38697         }
38698         this.fireEvent("paneladded", this, panel);
38699         return panel;
38700     },
38701
38702     /**
38703      * Hides the tab for the specified panel.
38704      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38705      */
38706     hidePanel : function(panel){
38707         if(this.tabs && (panel = this.getPanel(panel))){
38708             this.tabs.hideTab(panel.getEl().id);
38709         }
38710     },
38711
38712     /**
38713      * Unhides the tab for a previously hidden panel.
38714      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38715      */
38716     unhidePanel : function(panel){
38717         if(this.tabs && (panel = this.getPanel(panel))){
38718             this.tabs.unhideTab(panel.getEl().id);
38719         }
38720     },
38721
38722     clearPanels : function(){
38723         while(this.panels.getCount() > 0){
38724              this.remove(this.panels.first());
38725         }
38726     },
38727
38728     /**
38729      * Removes the specified panel. If preservePanel is not true (either here or in the config), the panel is destroyed.
38730      * @param {Number/String/ContentPanel} panel The panel's index, id or the panel itself
38731      * @param {Boolean} preservePanel Overrides the config preservePanel option
38732      * @return {Roo.ContentPanel} The panel that was removed
38733      */
38734     remove : function(panel, preservePanel)
38735     {
38736         panel = this.getPanel(panel);
38737         if(!panel){
38738             return null;
38739         }
38740         var e = {};
38741         this.fireEvent("beforeremove", this, panel, e);
38742         if(e.cancel === true){
38743             return null;
38744         }
38745         preservePanel = (typeof preservePanel != "undefined" ? preservePanel : (this.config.preservePanels === true || panel.preserve === true));
38746         var panelId = panel.getId();
38747         this.panels.removeKey(panelId);
38748         if(preservePanel){
38749             document.body.appendChild(panel.getEl().dom);
38750         }
38751         if(this.tabs){
38752             this.tabs.removeTab(panel.getEl().id);
38753         }else if (!preservePanel){
38754             this.bodyEl.dom.removeChild(panel.getEl().dom);
38755         }
38756         if(this.panels.getCount() == 1 && this.tabs && !this.config.alwaysShowTabs){
38757             var p = this.panels.first();
38758             var tempEl = document.createElement("div"); // temp holder to keep IE from deleting the node
38759             tempEl.appendChild(p.getEl().dom);
38760             this.bodyEl.update("");
38761             this.bodyEl.dom.appendChild(p.getEl().dom);
38762             tempEl = null;
38763             this.updateTitle(p.getTitle());
38764             this.tabs = null;
38765             this.bodyEl.setStyle("overflow", this.config.autoScroll ? "auto" : "hidden");
38766             this.setActivePanel(p);
38767         }
38768         panel.setRegion(null);
38769         if(this.activePanel == panel){
38770             this.activePanel = null;
38771         }
38772         if(this.config.autoDestroy !== false && preservePanel !== true){
38773             try{panel.destroy();}catch(e){}
38774         }
38775         this.fireEvent("panelremoved", this, panel);
38776         return panel;
38777     },
38778
38779     /**
38780      * Returns the TabPanel component used by this region
38781      * @return {Roo.TabPanel}
38782      */
38783     getTabs : function(){
38784         return this.tabs;
38785     },
38786
38787     createTool : function(parentEl, className){
38788         var btn = Roo.DomHelper.append(parentEl, {
38789             tag: "div",
38790             cls: "x-layout-tools-button",
38791             children: [ {
38792                 tag: "div",
38793                 cls: "roo-layout-tools-button-inner " + className,
38794                 html: "&#160;"
38795             }]
38796         }, true);
38797         btn.addClassOnOver("roo-layout-tools-button-over");
38798         return btn;
38799     }
38800 });/*
38801  * Based on:
38802  * Ext JS Library 1.1.1
38803  * Copyright(c) 2006-2007, Ext JS, LLC.
38804  *
38805  * Originally Released Under LGPL - original licence link has changed is not relivant.
38806  *
38807  * Fork - LGPL
38808  * <script type="text/javascript">
38809  */
38810  
38811
38812
38813 /**
38814  * @class Roo.SplitLayoutRegion
38815  * @extends Roo.LayoutRegion
38816  * Adds a splitbar and other (private) useful functionality to a {@link Roo.LayoutRegion}.
38817  */
38818 Roo.bootstrap.layout.Split = function(config){
38819     this.cursor = config.cursor;
38820     Roo.bootstrap.layout.Split.superclass.constructor.call(this, config);
38821 };
38822
38823 Roo.extend(Roo.bootstrap.layout.Split, Roo.bootstrap.layout.Region,
38824 {
38825     splitTip : "Drag to resize.",
38826     collapsibleSplitTip : "Drag to resize. Double click to hide.",
38827     useSplitTips : false,
38828
38829     applyConfig : function(config){
38830         Roo.bootstrap.layout.Split.superclass.applyConfig.call(this, config);
38831     },
38832     
38833     onRender : function(ctr,pos) {
38834         
38835         Roo.bootstrap.layout.Split.superclass.onRender.call(this, ctr,pos);
38836         if(!this.config.split){
38837             return;
38838         }
38839         if(!this.split){
38840             
38841             var splitEl = Roo.DomHelper.append(ctr.dom,  {
38842                             tag: "div",
38843                             id: this.el.id + "-split",
38844                             cls: "roo-layout-split roo-layout-split-"+this.position,
38845                             html: "&#160;"
38846             });
38847             /** The SplitBar for this region 
38848             * @type Roo.SplitBar */
38849             // does not exist yet...
38850             Roo.log([this.position, this.orientation]);
38851             
38852             this.split = new Roo.bootstrap.SplitBar({
38853                 dragElement : splitEl,
38854                 resizingElement: this.el,
38855                 orientation : this.orientation
38856             });
38857             
38858             this.split.on("moved", this.onSplitMove, this);
38859             this.split.useShim = this.config.useShim === true;
38860             this.split.getMaximumSize = this[this.position == 'north' || this.position == 'south' ? 'getVMaxSize' : 'getHMaxSize'].createDelegate(this);
38861             if(this.useSplitTips){
38862                 this.split.el.dom.title = this.config.collapsible ? this.collapsibleSplitTip : this.splitTip;
38863             }
38864             //if(config.collapsible){
38865             //    this.split.el.on("dblclick", this.collapse,  this);
38866             //}
38867         }
38868         if(typeof this.config.minSize != "undefined"){
38869             this.split.minSize = this.config.minSize;
38870         }
38871         if(typeof this.config.maxSize != "undefined"){
38872             this.split.maxSize = this.config.maxSize;
38873         }
38874         if(this.config.hideWhenEmpty || this.config.hidden || this.config.collapsed){
38875             this.hideSplitter();
38876         }
38877         
38878     },
38879
38880     getHMaxSize : function(){
38881          var cmax = this.config.maxSize || 10000;
38882          var center = this.mgr.getRegion("center");
38883          return Math.min(cmax, (this.el.getWidth()+center.getEl().getWidth())-center.getMinWidth());
38884     },
38885
38886     getVMaxSize : function(){
38887          var cmax = this.config.maxSize || 10000;
38888          var center = this.mgr.getRegion("center");
38889          return Math.min(cmax, (this.el.getHeight()+center.getEl().getHeight())-center.getMinHeight());
38890     },
38891
38892     onSplitMove : function(split, newSize){
38893         this.fireEvent("resized", this, newSize);
38894     },
38895     
38896     /** 
38897      * Returns the {@link Roo.SplitBar} for this region.
38898      * @return {Roo.SplitBar}
38899      */
38900     getSplitBar : function(){
38901         return this.split;
38902     },
38903     
38904     hide : function(){
38905         this.hideSplitter();
38906         Roo.bootstrap.layout.Split.superclass.hide.call(this);
38907     },
38908
38909     hideSplitter : function(){
38910         if(this.split){
38911             this.split.el.setLocation(-2000,-2000);
38912             this.split.el.hide();
38913         }
38914     },
38915
38916     show : function(){
38917         if(this.split){
38918             this.split.el.show();
38919         }
38920         Roo.bootstrap.layout.Split.superclass.show.call(this);
38921     },
38922     
38923     beforeSlide: function(){
38924         if(Roo.isGecko){// firefox overflow auto bug workaround
38925             this.bodyEl.clip();
38926             if(this.tabs) {
38927                 this.tabs.bodyEl.clip();
38928             }
38929             if(this.activePanel){
38930                 this.activePanel.getEl().clip();
38931                 
38932                 if(this.activePanel.beforeSlide){
38933                     this.activePanel.beforeSlide();
38934                 }
38935             }
38936         }
38937     },
38938     
38939     afterSlide : function(){
38940         if(Roo.isGecko){// firefox overflow auto bug workaround
38941             this.bodyEl.unclip();
38942             if(this.tabs) {
38943                 this.tabs.bodyEl.unclip();
38944             }
38945             if(this.activePanel){
38946                 this.activePanel.getEl().unclip();
38947                 if(this.activePanel.afterSlide){
38948                     this.activePanel.afterSlide();
38949                 }
38950             }
38951         }
38952     },
38953
38954     initAutoHide : function(){
38955         if(this.autoHide !== false){
38956             if(!this.autoHideHd){
38957                 var st = new Roo.util.DelayedTask(this.slideIn, this);
38958                 this.autoHideHd = {
38959                     "mouseout": function(e){
38960                         if(!e.within(this.el, true)){
38961                             st.delay(500);
38962                         }
38963                     },
38964                     "mouseover" : function(e){
38965                         st.cancel();
38966                     },
38967                     scope : this
38968                 };
38969             }
38970             this.el.on(this.autoHideHd);
38971         }
38972     },
38973
38974     clearAutoHide : function(){
38975         if(this.autoHide !== false){
38976             this.el.un("mouseout", this.autoHideHd.mouseout);
38977             this.el.un("mouseover", this.autoHideHd.mouseover);
38978         }
38979     },
38980
38981     clearMonitor : function(){
38982         Roo.get(document).un("click", this.slideInIf, this);
38983     },
38984
38985     // these names are backwards but not changed for compat
38986     slideOut : function(){
38987         if(this.isSlid || this.el.hasActiveFx()){
38988             return;
38989         }
38990         this.isSlid = true;
38991         if(this.collapseBtn){
38992             this.collapseBtn.hide();
38993         }
38994         this.closeBtnState = this.closeBtn.getStyle('display');
38995         this.closeBtn.hide();
38996         if(this.stickBtn){
38997             this.stickBtn.show();
38998         }
38999         this.el.show();
39000         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor());
39001         this.beforeSlide();
39002         this.el.setStyle("z-index", 10001);
39003         this.el.slideIn(this.getSlideAnchor(), {
39004             callback: function(){
39005                 this.afterSlide();
39006                 this.initAutoHide();
39007                 Roo.get(document).on("click", this.slideInIf, this);
39008                 this.fireEvent("slideshow", this);
39009             },
39010             scope: this,
39011             block: true
39012         });
39013     },
39014
39015     afterSlideIn : function(){
39016         this.clearAutoHide();
39017         this.isSlid = false;
39018         this.clearMonitor();
39019         this.el.setStyle("z-index", "");
39020         if(this.collapseBtn){
39021             this.collapseBtn.show();
39022         }
39023         this.closeBtn.setStyle('display', this.closeBtnState);
39024         if(this.stickBtn){
39025             this.stickBtn.hide();
39026         }
39027         this.fireEvent("slidehide", this);
39028     },
39029
39030     slideIn : function(cb){
39031         if(!this.isSlid || this.el.hasActiveFx()){
39032             Roo.callback(cb);
39033             return;
39034         }
39035         this.isSlid = false;
39036         this.beforeSlide();
39037         this.el.slideOut(this.getSlideAnchor(), {
39038             callback: function(){
39039                 this.el.setLeftTop(-10000, -10000);
39040                 this.afterSlide();
39041                 this.afterSlideIn();
39042                 Roo.callback(cb);
39043             },
39044             scope: this,
39045             block: true
39046         });
39047     },
39048     
39049     slideInIf : function(e){
39050         if(!e.within(this.el)){
39051             this.slideIn();
39052         }
39053     },
39054
39055     animateCollapse : function(){
39056         this.beforeSlide();
39057         this.el.setStyle("z-index", 20000);
39058         var anchor = this.getSlideAnchor();
39059         this.el.slideOut(anchor, {
39060             callback : function(){
39061                 this.el.setStyle("z-index", "");
39062                 this.collapsedEl.slideIn(anchor, {duration:.3});
39063                 this.afterSlide();
39064                 this.el.setLocation(-10000,-10000);
39065                 this.el.hide();
39066                 this.fireEvent("collapsed", this);
39067             },
39068             scope: this,
39069             block: true
39070         });
39071     },
39072
39073     animateExpand : function(){
39074         this.beforeSlide();
39075         this.el.alignTo(this.collapsedEl, this.getCollapseAnchor(), this.getExpandAdj());
39076         this.el.setStyle("z-index", 20000);
39077         this.collapsedEl.hide({
39078             duration:.1
39079         });
39080         this.el.slideIn(this.getSlideAnchor(), {
39081             callback : function(){
39082                 this.el.setStyle("z-index", "");
39083                 this.afterSlide();
39084                 if(this.split){
39085                     this.split.el.show();
39086                 }
39087                 this.fireEvent("invalidated", this);
39088                 this.fireEvent("expanded", this);
39089             },
39090             scope: this,
39091             block: true
39092         });
39093     },
39094
39095     anchors : {
39096         "west" : "left",
39097         "east" : "right",
39098         "north" : "top",
39099         "south" : "bottom"
39100     },
39101
39102     sanchors : {
39103         "west" : "l",
39104         "east" : "r",
39105         "north" : "t",
39106         "south" : "b"
39107     },
39108
39109     canchors : {
39110         "west" : "tl-tr",
39111         "east" : "tr-tl",
39112         "north" : "tl-bl",
39113         "south" : "bl-tl"
39114     },
39115
39116     getAnchor : function(){
39117         return this.anchors[this.position];
39118     },
39119
39120     getCollapseAnchor : function(){
39121         return this.canchors[this.position];
39122     },
39123
39124     getSlideAnchor : function(){
39125         return this.sanchors[this.position];
39126     },
39127
39128     getAlignAdj : function(){
39129         var cm = this.cmargins;
39130         switch(this.position){
39131             case "west":
39132                 return [0, 0];
39133             break;
39134             case "east":
39135                 return [0, 0];
39136             break;
39137             case "north":
39138                 return [0, 0];
39139             break;
39140             case "south":
39141                 return [0, 0];
39142             break;
39143         }
39144     },
39145
39146     getExpandAdj : function(){
39147         var c = this.collapsedEl, cm = this.cmargins;
39148         switch(this.position){
39149             case "west":
39150                 return [-(cm.right+c.getWidth()+cm.left), 0];
39151             break;
39152             case "east":
39153                 return [cm.right+c.getWidth()+cm.left, 0];
39154             break;
39155             case "north":
39156                 return [0, -(cm.top+cm.bottom+c.getHeight())];
39157             break;
39158             case "south":
39159                 return [0, cm.top+cm.bottom+c.getHeight()];
39160             break;
39161         }
39162     }
39163 });/*
39164  * Based on:
39165  * Ext JS Library 1.1.1
39166  * Copyright(c) 2006-2007, Ext JS, LLC.
39167  *
39168  * Originally Released Under LGPL - original licence link has changed is not relivant.
39169  *
39170  * Fork - LGPL
39171  * <script type="text/javascript">
39172  */
39173 /*
39174  * These classes are private internal classes
39175  */
39176 Roo.bootstrap.layout.Center = function(config){
39177     config.region = "center";
39178     Roo.bootstrap.layout.Region.call(this, config);
39179     this.visible = true;
39180     this.minWidth = config.minWidth || 20;
39181     this.minHeight = config.minHeight || 20;
39182 };
39183
39184 Roo.extend(Roo.bootstrap.layout.Center, Roo.bootstrap.layout.Region, {
39185     hide : function(){
39186         // center panel can't be hidden
39187     },
39188     
39189     show : function(){
39190         // center panel can't be hidden
39191     },
39192     
39193     getMinWidth: function(){
39194         return this.minWidth;
39195     },
39196     
39197     getMinHeight: function(){
39198         return this.minHeight;
39199     }
39200 });
39201
39202
39203
39204
39205  
39206
39207
39208
39209
39210
39211
39212 Roo.bootstrap.layout.North = function(config)
39213 {
39214     config.region = 'north';
39215     config.cursor = 'n-resize';
39216     
39217     Roo.bootstrap.layout.Split.call(this, config);
39218     
39219     
39220     if(this.split){
39221         this.split.placement = Roo.bootstrap.SplitBar.TOP;
39222         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39223         this.split.el.addClass("roo-layout-split-v");
39224     }
39225     var size = config.initialSize || config.height;
39226     if(typeof size != "undefined"){
39227         this.el.setHeight(size);
39228     }
39229 };
39230 Roo.extend(Roo.bootstrap.layout.North, Roo.bootstrap.layout.Split,
39231 {
39232     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39233     
39234     
39235     
39236     getBox : function(){
39237         if(this.collapsed){
39238             return this.collapsedEl.getBox();
39239         }
39240         var box = this.el.getBox();
39241         if(this.split){
39242             box.height += this.split.el.getHeight();
39243         }
39244         return box;
39245     },
39246     
39247     updateBox : function(box){
39248         if(this.split && !this.collapsed){
39249             box.height -= this.split.el.getHeight();
39250             this.split.el.setLeft(box.x);
39251             this.split.el.setTop(box.y+box.height);
39252             this.split.el.setWidth(box.width);
39253         }
39254         if(this.collapsed){
39255             this.updateBody(box.width, null);
39256         }
39257         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39258     }
39259 });
39260
39261
39262
39263
39264
39265 Roo.bootstrap.layout.South = function(config){
39266     config.region = 'south';
39267     config.cursor = 's-resize';
39268     Roo.bootstrap.layout.Split.call(this, config);
39269     if(this.split){
39270         this.split.placement = Roo.bootstrap.SplitBar.BOTTOM;
39271         this.split.orientation = Roo.bootstrap.SplitBar.VERTICAL;
39272         this.split.el.addClass("roo-layout-split-v");
39273     }
39274     var size = config.initialSize || config.height;
39275     if(typeof size != "undefined"){
39276         this.el.setHeight(size);
39277     }
39278 };
39279
39280 Roo.extend(Roo.bootstrap.layout.South, Roo.bootstrap.layout.Split, {
39281     orientation: Roo.bootstrap.SplitBar.VERTICAL,
39282     getBox : function(){
39283         if(this.collapsed){
39284             return this.collapsedEl.getBox();
39285         }
39286         var box = this.el.getBox();
39287         if(this.split){
39288             var sh = this.split.el.getHeight();
39289             box.height += sh;
39290             box.y -= sh;
39291         }
39292         return box;
39293     },
39294     
39295     updateBox : function(box){
39296         if(this.split && !this.collapsed){
39297             var sh = this.split.el.getHeight();
39298             box.height -= sh;
39299             box.y += sh;
39300             this.split.el.setLeft(box.x);
39301             this.split.el.setTop(box.y-sh);
39302             this.split.el.setWidth(box.width);
39303         }
39304         if(this.collapsed){
39305             this.updateBody(box.width, null);
39306         }
39307         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39308     }
39309 });
39310
39311 Roo.bootstrap.layout.East = function(config){
39312     config.region = "east";
39313     config.cursor = "e-resize";
39314     Roo.bootstrap.layout.Split.call(this, config);
39315     if(this.split){
39316         this.split.placement = Roo.bootstrap.SplitBar.RIGHT;
39317         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39318         this.split.el.addClass("roo-layout-split-h");
39319     }
39320     var size = config.initialSize || config.width;
39321     if(typeof size != "undefined"){
39322         this.el.setWidth(size);
39323     }
39324 };
39325 Roo.extend(Roo.bootstrap.layout.East, Roo.bootstrap.layout.Split, {
39326     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39327     getBox : function(){
39328         if(this.collapsed){
39329             return this.collapsedEl.getBox();
39330         }
39331         var box = this.el.getBox();
39332         if(this.split){
39333             var sw = this.split.el.getWidth();
39334             box.width += sw;
39335             box.x -= sw;
39336         }
39337         return box;
39338     },
39339
39340     updateBox : function(box){
39341         if(this.split && !this.collapsed){
39342             var sw = this.split.el.getWidth();
39343             box.width -= sw;
39344             this.split.el.setLeft(box.x);
39345             this.split.el.setTop(box.y);
39346             this.split.el.setHeight(box.height);
39347             box.x += sw;
39348         }
39349         if(this.collapsed){
39350             this.updateBody(null, box.height);
39351         }
39352         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39353     }
39354 });
39355
39356 Roo.bootstrap.layout.West = function(config){
39357     config.region = "west";
39358     config.cursor = "w-resize";
39359     
39360     Roo.bootstrap.layout.Split.call(this, config);
39361     if(this.split){
39362         this.split.placement = Roo.bootstrap.SplitBar.LEFT;
39363         this.split.orientation = Roo.bootstrap.SplitBar.HORIZONTAL;
39364         this.split.el.addClass("roo-layout-split-h");
39365     }
39366     
39367 };
39368 Roo.extend(Roo.bootstrap.layout.West, Roo.bootstrap.layout.Split, {
39369     orientation: Roo.bootstrap.SplitBar.HORIZONTAL,
39370     
39371     onRender: function(ctr, pos)
39372     {
39373         Roo.bootstrap.layout.West.superclass.onRender.call(this, ctr,pos);
39374         var size = this.config.initialSize || this.config.width;
39375         if(typeof size != "undefined"){
39376             this.el.setWidth(size);
39377         }
39378     },
39379     
39380     getBox : function(){
39381         if(this.collapsed){
39382             return this.collapsedEl.getBox();
39383         }
39384         var box = this.el.getBox();
39385         if(this.split){
39386             box.width += this.split.el.getWidth();
39387         }
39388         return box;
39389     },
39390     
39391     updateBox : function(box){
39392         if(this.split && !this.collapsed){
39393             var sw = this.split.el.getWidth();
39394             box.width -= sw;
39395             this.split.el.setLeft(box.x+box.width);
39396             this.split.el.setTop(box.y);
39397             this.split.el.setHeight(box.height);
39398         }
39399         if(this.collapsed){
39400             this.updateBody(null, box.height);
39401         }
39402         Roo.bootstrap.layout.Region.prototype.updateBox.call(this, box);
39403     }
39404 });Roo.namespace("Roo.bootstrap.panel");/*
39405  * Based on:
39406  * Ext JS Library 1.1.1
39407  * Copyright(c) 2006-2007, Ext JS, LLC.
39408  *
39409  * Originally Released Under LGPL - original licence link has changed is not relivant.
39410  *
39411  * Fork - LGPL
39412  * <script type="text/javascript">
39413  */
39414 /**
39415  * @class Roo.ContentPanel
39416  * @extends Roo.util.Observable
39417  * A basic ContentPanel element.
39418  * @cfg {Boolean}   fitToFrame    True for this panel to adjust its size to fit when the region resizes  (defaults to false)
39419  * @cfg {Boolean}   fitContainer   When using {@link #fitToFrame} and {@link #resizeEl}, you can also fit the parent container  (defaults to false)
39420  * @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
39421  * @cfg {Boolean}   closable      True if the panel can be closed/removed
39422  * @cfg {Boolean}   background    True if the panel should not be activated when it is added (defaults to false)
39423  * @cfg {String/HTMLElement/Element} resizeEl An element to resize if {@link #fitToFrame} is true (instead of this panel's element)
39424  * @cfg {Toolbar}   toolbar       A toolbar for this panel
39425  * @cfg {Boolean} autoScroll    True to scroll overflow in this panel (use with {@link #fitToFrame})
39426  * @cfg {String} title          The title for this panel
39427  * @cfg {Array} adjustments     Values to <b>add</b> to the width/height when doing a {@link #fitToFrame} (default is [0, 0])
39428  * @cfg {String} url            Calls {@link #setUrl} with this value
39429  * @cfg {String} region         (center|north|south|east|west) which region to put this panel on (when used with xtype constructors)
39430  * @cfg {String/Object} params  When used with {@link #url}, calls {@link #setUrl} with this value
39431  * @cfg {Boolean} loadOnce      When used with {@link #url}, calls {@link #setUrl} with this value
39432  * @cfg {String}    content        Raw content to fill content panel with (uses setContent on construction.)
39433  * @cfg {Boolean} badges render the badges
39434  * @cfg {String} cls  extra classes to use  
39435  * @cfg {String} background (primary|secondary|success|info|warning|danger|light|dark)
39436
39437  * @constructor
39438  * Create a new ContentPanel.
39439  * @param {String/HTMLElement/Roo.Element} el The container element for this panel
39440  * @param {String/Object} config A string to set only the title or a config object
39441  * @param {String} content (optional) Set the HTML content for this panel
39442  * @param {String} region (optional) Used by xtype constructors to add to regions. (values center,east,west,south,north)
39443  */
39444 Roo.bootstrap.panel.Content = function( config){
39445     
39446     this.tpl = config.tpl || false;
39447     
39448     var el = config.el;
39449     var content = config.content;
39450
39451     if(config.autoCreate){ // xtype is available if this is called from factory
39452         el = Roo.id();
39453     }
39454     this.el = Roo.get(el);
39455     if(!this.el && config && config.autoCreate){
39456         if(typeof config.autoCreate == "object"){
39457             if(!config.autoCreate.id){
39458                 config.autoCreate.id = config.id||el;
39459             }
39460             this.el = Roo.DomHelper.append(document.body,
39461                         config.autoCreate, true);
39462         }else{
39463             var elcfg =  {
39464                 tag: "div",
39465                 cls: (config.cls || '') +
39466                     (config.background ? ' bg-' + config.background : '') +
39467                     " roo-layout-inactive-content",
39468                 id: config.id||el
39469             };
39470             if (config.html) {
39471                 elcfg.html = config.html;
39472                 
39473             }
39474                         
39475             this.el = Roo.DomHelper.append(document.body, elcfg , true);
39476         }
39477     } 
39478     this.closable = false;
39479     this.loaded = false;
39480     this.active = false;
39481    
39482       
39483     if (config.toolbar && !config.toolbar.el && config.toolbar.xtype) {
39484         
39485         this.toolbar = new config.toolbar.xns[config.toolbar.xtype](config.toolbar);
39486         
39487         this.wrapEl = this.el; //this.el.wrap();
39488         var ti = [];
39489         if (config.toolbar.items) {
39490             ti = config.toolbar.items ;
39491             delete config.toolbar.items ;
39492         }
39493         
39494         var nitems = [];
39495         this.toolbar.render(this.wrapEl, 'before');
39496         for(var i =0;i < ti.length;i++) {
39497           //  Roo.log(['add child', items[i]]);
39498             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39499         }
39500         this.toolbar.items = nitems;
39501         this.toolbar.el.insertBefore(this.wrapEl.dom.firstChild);
39502         delete config.toolbar;
39503         
39504     }
39505     /*
39506     // xtype created footer. - not sure if will work as we normally have to render first..
39507     if (this.footer && !this.footer.el && this.footer.xtype) {
39508         if (!this.wrapEl) {
39509             this.wrapEl = this.el.wrap();
39510         }
39511     
39512         this.footer.container = this.wrapEl.createChild();
39513          
39514         this.footer = Roo.factory(this.footer, Roo);
39515         
39516     }
39517     */
39518     
39519      if(typeof config == "string"){
39520         this.title = config;
39521     }else{
39522         Roo.apply(this, config);
39523     }
39524     
39525     if(this.resizeEl){
39526         this.resizeEl = Roo.get(this.resizeEl, true);
39527     }else{
39528         this.resizeEl = this.el;
39529     }
39530     // handle view.xtype
39531     
39532  
39533     
39534     
39535     this.addEvents({
39536         /**
39537          * @event activate
39538          * Fires when this panel is activated. 
39539          * @param {Roo.ContentPanel} this
39540          */
39541         "activate" : true,
39542         /**
39543          * @event deactivate
39544          * Fires when this panel is activated. 
39545          * @param {Roo.ContentPanel} this
39546          */
39547         "deactivate" : true,
39548
39549         /**
39550          * @event resize
39551          * Fires when this panel is resized if fitToFrame is true.
39552          * @param {Roo.ContentPanel} this
39553          * @param {Number} width The width after any component adjustments
39554          * @param {Number} height The height after any component adjustments
39555          */
39556         "resize" : true,
39557         
39558          /**
39559          * @event render
39560          * Fires when this tab is created
39561          * @param {Roo.ContentPanel} this
39562          */
39563         "render" : true
39564         
39565         
39566         
39567     });
39568     
39569
39570     
39571     
39572     if(this.autoScroll){
39573         this.resizeEl.setStyle("overflow", "auto");
39574     } else {
39575         // fix randome scrolling
39576         //this.el.on('scroll', function() {
39577         //    Roo.log('fix random scolling');
39578         //    this.scrollTo('top',0); 
39579         //});
39580     }
39581     content = content || this.content;
39582     if(content){
39583         this.setContent(content);
39584     }
39585     if(config && config.url){
39586         this.setUrl(this.url, this.params, this.loadOnce);
39587     }
39588     
39589     
39590     
39591     Roo.bootstrap.panel.Content.superclass.constructor.call(this);
39592     
39593     if (this.view && typeof(this.view.xtype) != 'undefined') {
39594         this.view.el = this.el.appendChild(document.createElement("div"));
39595         this.view = Roo.factory(this.view); 
39596         this.view.render  &&  this.view.render(false, '');  
39597     }
39598     
39599     
39600     this.fireEvent('render', this);
39601 };
39602
39603 Roo.extend(Roo.bootstrap.panel.Content, Roo.bootstrap.Component, {
39604     
39605     cls : '',
39606     background : '',
39607     
39608     tabTip : '',
39609     
39610     setRegion : function(region){
39611         this.region = region;
39612         this.setActiveClass(region && !this.background);
39613     },
39614     
39615     
39616     setActiveClass: function(state)
39617     {
39618         if(state){
39619            this.el.replaceClass("roo-layout-inactive-content", "roo-layout-active-content");
39620            this.el.setStyle('position','relative');
39621         }else{
39622            this.el.replaceClass("roo-layout-active-content", "roo-layout-inactive-content");
39623            this.el.setStyle('position', 'absolute');
39624         } 
39625     },
39626     
39627     /**
39628      * Returns the toolbar for this Panel if one was configured. 
39629      * @return {Roo.Toolbar} 
39630      */
39631     getToolbar : function(){
39632         return this.toolbar;
39633     },
39634     
39635     setActiveState : function(active)
39636     {
39637         this.active = active;
39638         this.setActiveClass(active);
39639         if(!active){
39640             if(this.fireEvent("deactivate", this) === false){
39641                 return false;
39642             }
39643             return true;
39644         }
39645         this.fireEvent("activate", this);
39646         return true;
39647     },
39648     /**
39649      * Updates this panel's element
39650      * @param {String} content The new content
39651      * @param {Boolean} loadScripts (optional) true to look for and process scripts
39652     */
39653     setContent : function(content, loadScripts){
39654         this.el.update(content, loadScripts);
39655     },
39656
39657     ignoreResize : function(w, h){
39658         if(this.lastSize && this.lastSize.width == w && this.lastSize.height == h){
39659             return true;
39660         }else{
39661             this.lastSize = {width: w, height: h};
39662             return false;
39663         }
39664     },
39665     /**
39666      * Get the {@link Roo.UpdateManager} for this panel. Enables you to perform Ajax updates.
39667      * @return {Roo.UpdateManager} The UpdateManager
39668      */
39669     getUpdateManager : function(){
39670         return this.el.getUpdateManager();
39671     },
39672      /**
39673      * Loads this content panel immediately with content from XHR. Note: to delay loading until the panel is activated, use {@link #setUrl}.
39674      * @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:
39675 <pre><code>
39676 panel.load({
39677     url: "your-url.php",
39678     params: {param1: "foo", param2: "bar"}, // or a URL encoded string
39679     callback: yourFunction,
39680     scope: yourObject, //(optional scope)
39681     discardUrl: false,
39682     nocache: false,
39683     text: "Loading...",
39684     timeout: 30,
39685     scripts: false
39686 });
39687 </code></pre>
39688      * The only required property is <i>url</i>. The optional properties <i>nocache</i>, <i>text</i> and <i>scripts</i>
39689      * 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.
39690      * @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}
39691      * @param {Function} callback (optional) Callback when transaction is complete -- called with signature (oElement, bSuccess, oResponse)
39692      * @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.
39693      * @return {Roo.ContentPanel} this
39694      */
39695     load : function(){
39696         var um = this.el.getUpdateManager();
39697         um.update.apply(um, arguments);
39698         return this;
39699     },
39700
39701
39702     /**
39703      * 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.
39704      * @param {String/Function} url The URL to load the content from or a function to call to get the URL
39705      * @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)
39706      * @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)
39707      * @return {Roo.UpdateManager} The UpdateManager
39708      */
39709     setUrl : function(url, params, loadOnce){
39710         if(this.refreshDelegate){
39711             this.removeListener("activate", this.refreshDelegate);
39712         }
39713         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
39714         this.on("activate", this.refreshDelegate);
39715         return this.el.getUpdateManager();
39716     },
39717     
39718     _handleRefresh : function(url, params, loadOnce){
39719         if(!loadOnce || !this.loaded){
39720             var updater = this.el.getUpdateManager();
39721             updater.update(url, params, this._setLoaded.createDelegate(this));
39722         }
39723     },
39724     
39725     _setLoaded : function(){
39726         this.loaded = true;
39727     }, 
39728     
39729     /**
39730      * Returns this panel's id
39731      * @return {String} 
39732      */
39733     getId : function(){
39734         return this.el.id;
39735     },
39736     
39737     /** 
39738      * Returns this panel's element - used by regiosn to add.
39739      * @return {Roo.Element} 
39740      */
39741     getEl : function(){
39742         return this.wrapEl || this.el;
39743     },
39744     
39745    
39746     
39747     adjustForComponents : function(width, height)
39748     {
39749         //Roo.log('adjustForComponents ');
39750         if(this.resizeEl != this.el){
39751             width -= this.el.getFrameWidth('lr');
39752             height -= this.el.getFrameWidth('tb');
39753         }
39754         if(this.toolbar){
39755             var te = this.toolbar.getEl();
39756             te.setWidth(width);
39757             height -= te.getHeight();
39758         }
39759         if(this.footer){
39760             var te = this.footer.getEl();
39761             te.setWidth(width);
39762             height -= te.getHeight();
39763         }
39764         
39765         
39766         if(this.adjustments){
39767             width += this.adjustments[0];
39768             height += this.adjustments[1];
39769         }
39770         return {"width": width, "height": height};
39771     },
39772     
39773     setSize : function(width, height){
39774         if(this.fitToFrame && !this.ignoreResize(width, height)){
39775             if(this.fitContainer && this.resizeEl != this.el){
39776                 this.el.setSize(width, height);
39777             }
39778             var size = this.adjustForComponents(width, height);
39779             this.resizeEl.setSize(this.autoWidth ? "auto" : size.width, this.autoHeight ? "auto" : size.height);
39780             this.fireEvent('resize', this, size.width, size.height);
39781         }
39782     },
39783     
39784     /**
39785      * Returns this panel's title
39786      * @return {String} 
39787      */
39788     getTitle : function(){
39789         
39790         if (typeof(this.title) != 'object') {
39791             return this.title;
39792         }
39793         
39794         var t = '';
39795         for (var k in this.title) {
39796             if (!this.title.hasOwnProperty(k)) {
39797                 continue;
39798             }
39799             
39800             if (k.indexOf('-') >= 0) {
39801                 var s = k.split('-');
39802                 for (var i = 0; i<s.length; i++) {
39803                     t += "<span class='visible-"+s[i]+"'>"+this.title[k]+"</span>";
39804                 }
39805             } else {
39806                 t += "<span class='visible-"+k+"'>"+this.title[k]+"</span>";
39807             }
39808         }
39809         return t;
39810     },
39811     
39812     /**
39813      * Set this panel's title
39814      * @param {String} title
39815      */
39816     setTitle : function(title){
39817         this.title = title;
39818         if(this.region){
39819             this.region.updatePanelTitle(this, title);
39820         }
39821     },
39822     
39823     /**
39824      * Returns true is this panel was configured to be closable
39825      * @return {Boolean} 
39826      */
39827     isClosable : function(){
39828         return this.closable;
39829     },
39830     
39831     beforeSlide : function(){
39832         this.el.clip();
39833         this.resizeEl.clip();
39834     },
39835     
39836     afterSlide : function(){
39837         this.el.unclip();
39838         this.resizeEl.unclip();
39839     },
39840     
39841     /**
39842      *   Force a content refresh from the URL specified in the {@link #setUrl} method.
39843      *   Will fail silently if the {@link #setUrl} method has not been called.
39844      *   This does not activate the panel, just updates its content.
39845      */
39846     refresh : function(){
39847         if(this.refreshDelegate){
39848            this.loaded = false;
39849            this.refreshDelegate();
39850         }
39851     },
39852     
39853     /**
39854      * Destroys this panel
39855      */
39856     destroy : function(){
39857         this.el.removeAllListeners();
39858         var tempEl = document.createElement("span");
39859         tempEl.appendChild(this.el.dom);
39860         tempEl.innerHTML = "";
39861         this.el.remove();
39862         this.el = null;
39863     },
39864     
39865     /**
39866      * form - if the content panel contains a form - this is a reference to it.
39867      * @type {Roo.form.Form}
39868      */
39869     form : false,
39870     /**
39871      * view - if the content panel contains a view (Roo.DatePicker / Roo.View / Roo.JsonView)
39872      *    This contains a reference to it.
39873      * @type {Roo.View}
39874      */
39875     view : false,
39876     
39877       /**
39878      * Adds a xtype elements to the panel - currently only supports Forms, View, JsonView.
39879      * <pre><code>
39880
39881 layout.addxtype({
39882        xtype : 'Form',
39883        items: [ .... ]
39884    }
39885 );
39886
39887 </code></pre>
39888      * @param {Object} cfg Xtype definition of item to add.
39889      */
39890     
39891     
39892     getChildContainer: function () {
39893         return this.getEl();
39894     }
39895     
39896     
39897     /*
39898         var  ret = new Roo.factory(cfg);
39899         return ret;
39900         
39901         
39902         // add form..
39903         if (cfg.xtype.match(/^Form$/)) {
39904             
39905             var el;
39906             //if (this.footer) {
39907             //    el = this.footer.container.insertSibling(false, 'before');
39908             //} else {
39909                 el = this.el.createChild();
39910             //}
39911
39912             this.form = new  Roo.form.Form(cfg);
39913             
39914             
39915             if ( this.form.allItems.length) {
39916                 this.form.render(el.dom);
39917             }
39918             return this.form;
39919         }
39920         // should only have one of theses..
39921         if ([ 'View', 'JsonView', 'DatePicker'].indexOf(cfg.xtype) > -1) {
39922             // views.. should not be just added - used named prop 'view''
39923             
39924             cfg.el = this.el.appendChild(document.createElement("div"));
39925             // factory?
39926             
39927             var ret = new Roo.factory(cfg);
39928              
39929              ret.render && ret.render(false, ''); // render blank..
39930             this.view = ret;
39931             return ret;
39932         }
39933         return false;
39934     }
39935     \*/
39936 });
39937  
39938 /**
39939  * @class Roo.bootstrap.panel.Grid
39940  * @extends Roo.bootstrap.panel.Content
39941  * @constructor
39942  * Create a new GridPanel.
39943  * @cfg {Roo.bootstrap.Table} grid The grid for this panel
39944  * @param {Object} config A the config object
39945   
39946  */
39947
39948
39949
39950 Roo.bootstrap.panel.Grid = function(config)
39951 {
39952     
39953       
39954     this.wrapper = Roo.DomHelper.append(document.body, // wrapper for IE7 strict & safari scroll issue
39955         {tag: "div", cls: "roo-layout-grid-wrapper roo-layout-inactive-content"}, true);
39956
39957     config.el = this.wrapper;
39958     //this.el = this.wrapper;
39959     
39960       if (config.container) {
39961         // ctor'ed from a Border/panel.grid
39962         
39963         
39964         this.wrapper.setStyle("overflow", "hidden");
39965         this.wrapper.addClass('roo-grid-container');
39966
39967     }
39968     
39969     
39970     if(config.toolbar){
39971         var tool_el = this.wrapper.createChild();    
39972         this.toolbar = Roo.factory(config.toolbar);
39973         var ti = [];
39974         if (config.toolbar.items) {
39975             ti = config.toolbar.items ;
39976             delete config.toolbar.items ;
39977         }
39978         
39979         var nitems = [];
39980         this.toolbar.render(tool_el);
39981         for(var i =0;i < ti.length;i++) {
39982           //  Roo.log(['add child', items[i]]);
39983             nitems.push(this.toolbar.addxtype(Roo.apply({}, ti[i])));
39984         }
39985         this.toolbar.items = nitems;
39986         
39987         delete config.toolbar;
39988     }
39989     
39990     Roo.bootstrap.panel.Grid.superclass.constructor.call(this, config);
39991     config.grid.scrollBody = true;;
39992     config.grid.monitorWindowResize = false; // turn off autosizing
39993     config.grid.autoHeight = false;
39994     config.grid.autoWidth = false;
39995     
39996     this.grid = new config.grid.xns[config.grid.xtype](config.grid);
39997     
39998     if (config.background) {
39999         // render grid on panel activation (if panel background)
40000         this.on('activate', function(gp) {
40001             if (!gp.grid.rendered) {
40002                 gp.grid.render(this.wrapper);
40003                 gp.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");   
40004             }
40005         });
40006             
40007     } else {
40008         this.grid.render(this.wrapper);
40009         this.grid.getGridEl().replaceClass("roo-layout-inactive-content", "roo-layout-component-panel");               
40010
40011     }
40012     //this.wrapper.dom.appendChild(config.grid.getGridEl().dom);
40013     // ??? needed ??? config.el = this.wrapper;
40014     
40015     
40016     
40017   
40018     // xtype created footer. - not sure if will work as we normally have to render first..
40019     if (this.footer && !this.footer.el && this.footer.xtype) {
40020         
40021         var ctr = this.grid.getView().getFooterPanel(true);
40022         this.footer.dataSource = this.grid.dataSource;
40023         this.footer = Roo.factory(this.footer, Roo);
40024         this.footer.render(ctr);
40025         
40026     }
40027     
40028     
40029     
40030     
40031      
40032 };
40033
40034 Roo.extend(Roo.bootstrap.panel.Grid, Roo.bootstrap.panel.Content, {
40035     getId : function(){
40036         return this.grid.id;
40037     },
40038     
40039     /**
40040      * Returns the grid for this panel
40041      * @return {Roo.bootstrap.Table} 
40042      */
40043     getGrid : function(){
40044         return this.grid;    
40045     },
40046     
40047     setSize : function(width, height){
40048         if(!this.ignoreResize(width, height)){
40049             var grid = this.grid;
40050             var size = this.adjustForComponents(width, height);
40051             // tfoot is not a footer?
40052           
40053             
40054             var gridel = grid.getGridEl();
40055             gridel.setSize(size.width, size.height);
40056             
40057             var tbd = grid.getGridEl().select('tbody', true).first();
40058             var thd = grid.getGridEl().select('thead',true).first();
40059             var tbf= grid.getGridEl().select('tfoot', true).first();
40060
40061             if (tbf) {
40062                 size.height -= thd.getHeight();
40063             }
40064             if (thd) {
40065                 size.height -= thd.getHeight();
40066             }
40067             
40068             tbd.setSize(size.width, size.height );
40069             // this is for the account management tab -seems to work there.
40070             var thd = grid.getGridEl().select('thead',true).first();
40071             //if (tbd) {
40072             //    tbd.setSize(size.width, size.height - thd.getHeight());
40073             //}
40074              
40075             grid.autoSize();
40076         }
40077     },
40078      
40079     
40080     
40081     beforeSlide : function(){
40082         this.grid.getView().scroller.clip();
40083     },
40084     
40085     afterSlide : function(){
40086         this.grid.getView().scroller.unclip();
40087     },
40088     
40089     destroy : function(){
40090         this.grid.destroy();
40091         delete this.grid;
40092         Roo.bootstrap.panel.Grid.superclass.destroy.call(this); 
40093     }
40094 });
40095
40096 /**
40097  * @class Roo.bootstrap.panel.Nest
40098  * @extends Roo.bootstrap.panel.Content
40099  * @constructor
40100  * Create a new Panel, that can contain a layout.Border.
40101  * 
40102  * 
40103  * @param {Roo.BorderLayout} layout The layout for this panel
40104  * @param {String/Object} config A string to set only the title or a config object
40105  */
40106 Roo.bootstrap.panel.Nest = function(config)
40107 {
40108     // construct with only one argument..
40109     /* FIXME - implement nicer consturctors
40110     if (layout.layout) {
40111         config = layout;
40112         layout = config.layout;
40113         delete config.layout;
40114     }
40115     if (layout.xtype && !layout.getEl) {
40116         // then layout needs constructing..
40117         layout = Roo.factory(layout, Roo);
40118     }
40119     */
40120     
40121     config.el =  config.layout.getEl();
40122     
40123     Roo.bootstrap.panel.Nest.superclass.constructor.call(this, config);
40124     
40125     config.layout.monitorWindowResize = false; // turn off autosizing
40126     this.layout = config.layout;
40127     this.layout.getEl().addClass("roo-layout-nested-layout");
40128     this.layout.parent = this;
40129     
40130     
40131     
40132     
40133 };
40134
40135 Roo.extend(Roo.bootstrap.panel.Nest, Roo.bootstrap.panel.Content, {
40136
40137     setSize : function(width, height){
40138         if(!this.ignoreResize(width, height)){
40139             var size = this.adjustForComponents(width, height);
40140             var el = this.layout.getEl();
40141             if (size.height < 1) {
40142                 el.setWidth(size.width);   
40143             } else {
40144                 el.setSize(size.width, size.height);
40145             }
40146             var touch = el.dom.offsetWidth;
40147             this.layout.layout();
40148             // ie requires a double layout on the first pass
40149             if(Roo.isIE && !this.initialized){
40150                 this.initialized = true;
40151                 this.layout.layout();
40152             }
40153         }
40154     },
40155     
40156     // activate all subpanels if not currently active..
40157     
40158     setActiveState : function(active){
40159         this.active = active;
40160         this.setActiveClass(active);
40161         
40162         if(!active){
40163             this.fireEvent("deactivate", this);
40164             return;
40165         }
40166         
40167         this.fireEvent("activate", this);
40168         // not sure if this should happen before or after..
40169         if (!this.layout) {
40170             return; // should not happen..
40171         }
40172         var reg = false;
40173         for (var r in this.layout.regions) {
40174             reg = this.layout.getRegion(r);
40175             if (reg.getActivePanel()) {
40176                 //reg.showPanel(reg.getActivePanel()); // force it to activate.. 
40177                 reg.setActivePanel(reg.getActivePanel());
40178                 continue;
40179             }
40180             if (!reg.panels.length) {
40181                 continue;
40182             }
40183             reg.showPanel(reg.getPanel(0));
40184         }
40185         
40186         
40187         
40188         
40189     },
40190     
40191     /**
40192      * Returns the nested BorderLayout for this panel
40193      * @return {Roo.BorderLayout} 
40194      */
40195     getLayout : function(){
40196         return this.layout;
40197     },
40198     
40199      /**
40200      * Adds a xtype elements to the layout of the nested panel
40201      * <pre><code>
40202
40203 panel.addxtype({
40204        xtype : 'ContentPanel',
40205        region: 'west',
40206        items: [ .... ]
40207    }
40208 );
40209
40210 panel.addxtype({
40211         xtype : 'NestedLayoutPanel',
40212         region: 'west',
40213         layout: {
40214            center: { },
40215            west: { }   
40216         },
40217         items : [ ... list of content panels or nested layout panels.. ]
40218    }
40219 );
40220 </code></pre>
40221      * @param {Object} cfg Xtype definition of item to add.
40222      */
40223     addxtype : function(cfg) {
40224         return this.layout.addxtype(cfg);
40225     
40226     }
40227 });/*
40228  * Based on:
40229  * Ext JS Library 1.1.1
40230  * Copyright(c) 2006-2007, Ext JS, LLC.
40231  *
40232  * Originally Released Under LGPL - original licence link has changed is not relivant.
40233  *
40234  * Fork - LGPL
40235  * <script type="text/javascript">
40236  */
40237 /**
40238  * @class Roo.TabPanel
40239  * @extends Roo.util.Observable
40240  * A lightweight tab container.
40241  * <br><br>
40242  * Usage:
40243  * <pre><code>
40244 // basic tabs 1, built from existing content
40245 var tabs = new Roo.TabPanel("tabs1");
40246 tabs.addTab("script", "View Script");
40247 tabs.addTab("markup", "View Markup");
40248 tabs.activate("script");
40249
40250 // more advanced tabs, built from javascript
40251 var jtabs = new Roo.TabPanel("jtabs");
40252 jtabs.addTab("jtabs-1", "Normal Tab", "My content was added during construction.");
40253
40254 // set up the UpdateManager
40255 var tab2 = jtabs.addTab("jtabs-2", "Ajax Tab 1");
40256 var updater = tab2.getUpdateManager();
40257 updater.setDefaultUrl("ajax1.htm");
40258 tab2.on('activate', updater.refresh, updater, true);
40259
40260 // Use setUrl for Ajax loading
40261 var tab3 = jtabs.addTab("jtabs-3", "Ajax Tab 2");
40262 tab3.setUrl("ajax2.htm", null, true);
40263
40264 // Disabled tab
40265 var tab4 = jtabs.addTab("tabs1-5", "Disabled Tab", "Can't see me cause I'm disabled");
40266 tab4.disable();
40267
40268 jtabs.activate("jtabs-1");
40269  * </code></pre>
40270  * @constructor
40271  * Create a new TabPanel.
40272  * @param {String/HTMLElement/Roo.Element} container The id, DOM element or Roo.Element container where this TabPanel is to be rendered.
40273  * @param {Object/Boolean} config Config object to set any properties for this TabPanel, or true to render the tabs on the bottom.
40274  */
40275 Roo.bootstrap.panel.Tabs = function(config){
40276     /**
40277     * The container element for this TabPanel.
40278     * @type Roo.Element
40279     */
40280     this.el = Roo.get(config.el);
40281     delete config.el;
40282     if(config){
40283         if(typeof config == "boolean"){
40284             this.tabPosition = config ? "bottom" : "top";
40285         }else{
40286             Roo.apply(this, config);
40287         }
40288     }
40289     
40290     if(this.tabPosition == "bottom"){
40291         // if tabs are at the bottom = create the body first.
40292         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40293         this.el.addClass("roo-tabs-bottom");
40294     }
40295     // next create the tabs holders
40296     
40297     if (this.tabPosition == "west"){
40298         
40299         var reg = this.region; // fake it..
40300         while (reg) {
40301             if (!reg.mgr.parent) {
40302                 break;
40303             }
40304             reg = reg.mgr.parent.region;
40305         }
40306         Roo.log("got nest?");
40307         Roo.log(reg);
40308         if (reg.mgr.getRegion('west')) {
40309             var ctrdom = reg.mgr.getRegion('west').bodyEl.dom;
40310             this.stripWrap = Roo.get(this.createStrip(ctrdom ), true);
40311             this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40312             this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40313             this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40314         
40315             
40316         }
40317         
40318         
40319     } else {
40320      
40321         this.stripWrap = Roo.get(this.createStrip(this.el.dom), true);
40322         this.stripEl = Roo.get(this.createStripList(this.stripWrap.dom), true);
40323         this.stripEl.setVisibilityMode(Roo.Element.DISPLAY);
40324         this.stripBody = Roo.get(this.stripWrap.dom.firstChild.firstChild, true);
40325     }
40326     
40327     
40328     if(Roo.isIE){
40329         Roo.fly(this.stripWrap.dom.firstChild).setStyle("overflow-x", "hidden");
40330     }
40331     
40332     // finally - if tabs are at the top, then create the body last..
40333     if(this.tabPosition != "bottom"){
40334         /** The body element that contains {@link Roo.TabPanelItem} bodies. +
40335          * @type Roo.Element
40336          */
40337         this.bodyEl = Roo.get(this.createBody(this.el.dom));
40338         this.el.addClass("roo-tabs-top");
40339     }
40340     this.items = [];
40341
40342     this.bodyEl.setStyle("position", "relative");
40343
40344     this.active = null;
40345     this.activateDelegate = this.activate.createDelegate(this);
40346
40347     this.addEvents({
40348         /**
40349          * @event tabchange
40350          * Fires when the active tab changes
40351          * @param {Roo.TabPanel} this
40352          * @param {Roo.TabPanelItem} activePanel The new active tab
40353          */
40354         "tabchange": true,
40355         /**
40356          * @event beforetabchange
40357          * Fires before the active tab changes, set cancel to true on the "e" parameter to cancel the change
40358          * @param {Roo.TabPanel} this
40359          * @param {Object} e Set cancel to true on this object to cancel the tab change
40360          * @param {Roo.TabPanelItem} tab The tab being changed to
40361          */
40362         "beforetabchange" : true
40363     });
40364
40365     Roo.EventManager.onWindowResize(this.onResize, this);
40366     this.cpad = this.el.getPadding("lr");
40367     this.hiddenCount = 0;
40368
40369
40370     // toolbar on the tabbar support...
40371     if (this.toolbar) {
40372         alert("no toolbar support yet");
40373         this.toolbar  = false;
40374         /*
40375         var tcfg = this.toolbar;
40376         tcfg.container = this.stripEl.child('td.x-tab-strip-toolbar');  
40377         this.toolbar = new Roo.Toolbar(tcfg);
40378         if (Roo.isSafari) {
40379             var tbl = tcfg.container.child('table', true);
40380             tbl.setAttribute('width', '100%');
40381         }
40382         */
40383         
40384     }
40385    
40386
40387
40388     Roo.bootstrap.panel.Tabs.superclass.constructor.call(this);
40389 };
40390
40391 Roo.extend(Roo.bootstrap.panel.Tabs, Roo.util.Observable, {
40392     /*
40393      *@cfg {String} tabPosition "top" or "bottom" (defaults to "top")
40394      */
40395     tabPosition : "top",
40396     /*
40397      *@cfg {Number} currentTabWidth The width of the current tab (defaults to 0)
40398      */
40399     currentTabWidth : 0,
40400     /*
40401      *@cfg {Number} minTabWidth The minimum width of a tab (defaults to 40) (ignored if {@link #resizeTabs} is not true)
40402      */
40403     minTabWidth : 40,
40404     /*
40405      *@cfg {Number} maxTabWidth The maximum width of a tab (defaults to 250) (ignored if {@link #resizeTabs} is not true)
40406      */
40407     maxTabWidth : 250,
40408     /*
40409      *@cfg {Number} preferredTabWidth The preferred (default) width of a tab (defaults to 175) (ignored if {@link #resizeTabs} is not true)
40410      */
40411     preferredTabWidth : 175,
40412     /*
40413      *@cfg {Boolean} resizeTabs True to enable dynamic tab resizing (defaults to false)
40414      */
40415     resizeTabs : false,
40416     /*
40417      *@cfg {Boolean} monitorResize Set this to true to turn on window resize monitoring (ignored if {@link #resizeTabs} is not true) (defaults to true)
40418      */
40419     monitorResize : true,
40420     /*
40421      *@cfg {Object} toolbar xtype description of toolbar to show at the right of the tab bar. 
40422      */
40423     toolbar : false,  // set by caller..
40424     
40425     region : false, /// set by caller
40426     
40427     disableTooltips : true, // not used yet...
40428
40429     /**
40430      * Creates a new {@link Roo.TabPanelItem} by looking for an existing element with the provided id -- if it's not found it creates one.
40431      * @param {String} id The id of the div to use <b>or create</b>
40432      * @param {String} text The text for the tab
40433      * @param {String} content (optional) Content to put in the TabPanelItem body
40434      * @param {Boolean} closable (optional) True to create a close icon on the tab
40435      * @return {Roo.TabPanelItem} The created TabPanelItem
40436      */
40437     addTab : function(id, text, content, closable, tpl)
40438     {
40439         var item = new Roo.bootstrap.panel.TabItem({
40440             panel: this,
40441             id : id,
40442             text : text,
40443             closable : closable,
40444             tpl : tpl
40445         });
40446         this.addTabItem(item);
40447         if(content){
40448             item.setContent(content);
40449         }
40450         return item;
40451     },
40452
40453     /**
40454      * Returns the {@link Roo.TabPanelItem} with the specified id/index
40455      * @param {String/Number} id The id or index of the TabPanelItem to fetch.
40456      * @return {Roo.TabPanelItem}
40457      */
40458     getTab : function(id){
40459         return this.items[id];
40460     },
40461
40462     /**
40463      * Hides the {@link Roo.TabPanelItem} with the specified id/index
40464      * @param {String/Number} id The id or index of the TabPanelItem to hide.
40465      */
40466     hideTab : function(id){
40467         var t = this.items[id];
40468         if(!t.isHidden()){
40469            t.setHidden(true);
40470            this.hiddenCount++;
40471            this.autoSizeTabs();
40472         }
40473     },
40474
40475     /**
40476      * "Unhides" the {@link Roo.TabPanelItem} with the specified id/index.
40477      * @param {String/Number} id The id or index of the TabPanelItem to unhide.
40478      */
40479     unhideTab : function(id){
40480         var t = this.items[id];
40481         if(t.isHidden()){
40482            t.setHidden(false);
40483            this.hiddenCount--;
40484            this.autoSizeTabs();
40485         }
40486     },
40487
40488     /**
40489      * Adds an existing {@link Roo.TabPanelItem}.
40490      * @param {Roo.TabPanelItem} item The TabPanelItem to add
40491      */
40492     addTabItem : function(item)
40493     {
40494         this.items[item.id] = item;
40495         this.items.push(item);
40496         this.autoSizeTabs();
40497       //  if(this.resizeTabs){
40498     //       item.setWidth(this.currentTabWidth || this.preferredTabWidth);
40499   //         this.autoSizeTabs();
40500 //        }else{
40501 //            item.autoSize();
40502        // }
40503     },
40504
40505     /**
40506      * Removes a {@link Roo.TabPanelItem}.
40507      * @param {String/Number} id The id or index of the TabPanelItem to remove.
40508      */
40509     removeTab : function(id){
40510         var items = this.items;
40511         var tab = items[id];
40512         if(!tab) { return; }
40513         var index = items.indexOf(tab);
40514         if(this.active == tab && items.length > 1){
40515             var newTab = this.getNextAvailable(index);
40516             if(newTab) {
40517                 newTab.activate();
40518             }
40519         }
40520         this.stripEl.dom.removeChild(tab.pnode.dom);
40521         if(tab.bodyEl.dom.parentNode == this.bodyEl.dom){ // if it was moved already prevent error
40522             this.bodyEl.dom.removeChild(tab.bodyEl.dom);
40523         }
40524         items.splice(index, 1);
40525         delete this.items[tab.id];
40526         tab.fireEvent("close", tab);
40527         tab.purgeListeners();
40528         this.autoSizeTabs();
40529     },
40530
40531     getNextAvailable : function(start){
40532         var items = this.items;
40533         var index = start;
40534         // look for a next tab that will slide over to
40535         // replace the one being removed
40536         while(index < items.length){
40537             var item = items[++index];
40538             if(item && !item.isHidden()){
40539                 return item;
40540             }
40541         }
40542         // if one isn't found select the previous tab (on the left)
40543         index = start;
40544         while(index >= 0){
40545             var item = items[--index];
40546             if(item && !item.isHidden()){
40547                 return item;
40548             }
40549         }
40550         return null;
40551     },
40552
40553     /**
40554      * Disables a {@link Roo.TabPanelItem}. It cannot be the active tab, if it is this call is ignored.
40555      * @param {String/Number} id The id or index of the TabPanelItem to disable.
40556      */
40557     disableTab : function(id){
40558         var tab = this.items[id];
40559         if(tab && this.active != tab){
40560             tab.disable();
40561         }
40562     },
40563
40564     /**
40565      * Enables a {@link Roo.TabPanelItem} that is disabled.
40566      * @param {String/Number} id The id or index of the TabPanelItem to enable.
40567      */
40568     enableTab : function(id){
40569         var tab = this.items[id];
40570         tab.enable();
40571     },
40572
40573     /**
40574      * Activates a {@link Roo.TabPanelItem}. The currently active one will be deactivated.
40575      * @param {String/Number} id The id or index of the TabPanelItem to activate.
40576      * @return {Roo.TabPanelItem} The TabPanelItem.
40577      */
40578     activate : function(id)
40579     {
40580         //Roo.log('activite:'  + id);
40581         
40582         var tab = this.items[id];
40583         if(!tab){
40584             return null;
40585         }
40586         if(tab == this.active || tab.disabled){
40587             return tab;
40588         }
40589         var e = {};
40590         this.fireEvent("beforetabchange", this, e, tab);
40591         if(e.cancel !== true && !tab.disabled){
40592             if(this.active){
40593                 this.active.hide();
40594             }
40595             this.active = this.items[id];
40596             this.active.show();
40597             this.fireEvent("tabchange", this, this.active);
40598         }
40599         return tab;
40600     },
40601
40602     /**
40603      * Gets the active {@link Roo.TabPanelItem}.
40604      * @return {Roo.TabPanelItem} The active TabPanelItem or null if none are active.
40605      */
40606     getActiveTab : function(){
40607         return this.active;
40608     },
40609
40610     /**
40611      * Updates the tab body element to fit the height of the container element
40612      * for overflow scrolling
40613      * @param {Number} targetHeight (optional) Override the starting height from the elements height
40614      */
40615     syncHeight : function(targetHeight){
40616         var height = (targetHeight || this.el.getHeight())-this.el.getBorderWidth("tb")-this.el.getPadding("tb");
40617         var bm = this.bodyEl.getMargins();
40618         var newHeight = height-(this.stripWrap.getHeight()||0)-(bm.top+bm.bottom);
40619         this.bodyEl.setHeight(newHeight);
40620         return newHeight;
40621     },
40622
40623     onResize : function(){
40624         if(this.monitorResize){
40625             this.autoSizeTabs();
40626         }
40627     },
40628
40629     /**
40630      * Disables tab resizing while tabs are being added (if {@link #resizeTabs} is false this does nothing)
40631      */
40632     beginUpdate : function(){
40633         this.updating = true;
40634     },
40635
40636     /**
40637      * Stops an update and resizes the tabs (if {@link #resizeTabs} is false this does nothing)
40638      */
40639     endUpdate : function(){
40640         this.updating = false;
40641         this.autoSizeTabs();
40642     },
40643
40644     /**
40645      * Manual call to resize the tabs (if {@link #resizeTabs} is false this does nothing)
40646      */
40647     autoSizeTabs : function()
40648     {
40649         var count = this.items.length;
40650         var vcount = count - this.hiddenCount;
40651         
40652         if (vcount < 2) {
40653             this.stripEl.hide();
40654         } else {
40655             this.stripEl.show();
40656         }
40657         
40658         if(!this.resizeTabs || count < 1 || vcount < 1 || this.updating) {
40659             return;
40660         }
40661         
40662         
40663         var w = Math.max(this.el.getWidth() - this.cpad, 10);
40664         var availWidth = Math.floor(w / vcount);
40665         var b = this.stripBody;
40666         if(b.getWidth() > w){
40667             var tabs = this.items;
40668             this.setTabWidth(Math.max(availWidth, this.minTabWidth)-2);
40669             if(availWidth < this.minTabWidth){
40670                 /*if(!this.sleft){    // incomplete scrolling code
40671                     this.createScrollButtons();
40672                 }
40673                 this.showScroll();
40674                 this.stripClip.setWidth(w - (this.sleft.getWidth()+this.sright.getWidth()));*/
40675             }
40676         }else{
40677             if(this.currentTabWidth < this.preferredTabWidth){
40678                 this.setTabWidth(Math.min(availWidth, this.preferredTabWidth)-2);
40679             }
40680         }
40681     },
40682
40683     /**
40684      * Returns the number of tabs in this TabPanel.
40685      * @return {Number}
40686      */
40687      getCount : function(){
40688          return this.items.length;
40689      },
40690
40691     /**
40692      * Resizes all the tabs to the passed width
40693      * @param {Number} The new width
40694      */
40695     setTabWidth : function(width){
40696         this.currentTabWidth = width;
40697         for(var i = 0, len = this.items.length; i < len; i++) {
40698                 if(!this.items[i].isHidden()) {
40699                 this.items[i].setWidth(width);
40700             }
40701         }
40702     },
40703
40704     /**
40705      * Destroys this TabPanel
40706      * @param {Boolean} removeEl (optional) True to remove the element from the DOM as well (defaults to undefined)
40707      */
40708     destroy : function(removeEl){
40709         Roo.EventManager.removeResizeListener(this.onResize, this);
40710         for(var i = 0, len = this.items.length; i < len; i++){
40711             this.items[i].purgeListeners();
40712         }
40713         if(removeEl === true){
40714             this.el.update("");
40715             this.el.remove();
40716         }
40717     },
40718     
40719     createStrip : function(container)
40720     {
40721         var strip = document.createElement("nav");
40722         strip.className = Roo.bootstrap.version == 4 ?
40723             "navbar-light bg-light" : 
40724             "navbar navbar-default"; //"x-tabs-wrap";
40725         container.appendChild(strip);
40726         return strip;
40727     },
40728     
40729     createStripList : function(strip)
40730     {
40731         // div wrapper for retard IE
40732         // returns the "tr" element.
40733         strip.innerHTML = '<ul class="nav nav-tabs" role="tablist"></ul>';
40734         //'<div class="x-tabs-strip-wrap">'+
40735           //  '<table class="x-tabs-strip" cellspacing="0" cellpadding="0" border="0"><tbody><tr>'+
40736           //  '<td class="x-tab-strip-toolbar"></td></tr></tbody></table></div>';
40737         return strip.firstChild; //.firstChild.firstChild.firstChild;
40738     },
40739     createBody : function(container)
40740     {
40741         var body = document.createElement("div");
40742         Roo.id(body, "tab-body");
40743         //Roo.fly(body).addClass("x-tabs-body");
40744         Roo.fly(body).addClass("tab-content");
40745         container.appendChild(body);
40746         return body;
40747     },
40748     createItemBody :function(bodyEl, id){
40749         var body = Roo.getDom(id);
40750         if(!body){
40751             body = document.createElement("div");
40752             body.id = id;
40753         }
40754         //Roo.fly(body).addClass("x-tabs-item-body");
40755         Roo.fly(body).addClass("tab-pane");
40756          bodyEl.insertBefore(body, bodyEl.firstChild);
40757         return body;
40758     },
40759     /** @private */
40760     createStripElements :  function(stripEl, text, closable, tpl)
40761     {
40762         var td = document.createElement("li"); // was td..
40763         td.className = 'nav-item';
40764         
40765         //stripEl.insertBefore(td, stripEl.childNodes[stripEl.childNodes.length-1]);
40766         
40767         
40768         stripEl.appendChild(td);
40769         /*if(closable){
40770             td.className = "x-tabs-closable";
40771             if(!this.closeTpl){
40772                 this.closeTpl = new Roo.Template(
40773                    '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40774                    '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span>' +
40775                    '<div unselectable="on" class="close-icon">&#160;</div></em></span></a>'
40776                 );
40777             }
40778             var el = this.closeTpl.overwrite(td, {"text": text});
40779             var close = el.getElementsByTagName("div")[0];
40780             var inner = el.getElementsByTagName("em")[0];
40781             return {"el": el, "close": close, "inner": inner};
40782         } else {
40783         */
40784         // not sure what this is..
40785 //            if(!this.tabTpl){
40786                 //this.tabTpl = new Roo.Template(
40787                 //   '<a href="#" class="x-tabs-right"><span class="x-tabs-left"><em class="x-tabs-inner">' +
40788                 //   '<span unselectable="on"' + (this.disableTooltips ? '' : ' title="{text}"') +' class="x-tabs-text">{text}</span></em></span></a>'
40789                 //);
40790 //                this.tabTpl = new Roo.Template(
40791 //                   '<a href="#">' +
40792 //                   '<span unselectable="on"' +
40793 //                            (this.disableTooltips ? '' : ' title="{text}"') +
40794 //                            ' >{text}</span></a>'
40795 //                );
40796 //                
40797 //            }
40798
40799
40800             var template = tpl || this.tabTpl || false;
40801             
40802             if(!template){
40803                 template =  new Roo.Template(
40804                         Roo.bootstrap.version == 4 ? 
40805                             (
40806                                 '<a class="nav-link" href="#" unselectable="on"' +
40807                                      (this.disableTooltips ? '' : ' title="{text}"') +
40808                                      ' >{text}</a>'
40809                             ) : (
40810                                 '<a class="nav-link" href="#">' +
40811                                 '<span unselectable="on"' +
40812                                          (this.disableTooltips ? '' : ' title="{text}"') +
40813                                     ' >{text}</span></a>'
40814                             )
40815                 );
40816             }
40817             
40818             switch (typeof(template)) {
40819                 case 'object' :
40820                     break;
40821                 case 'string' :
40822                     template = new Roo.Template(template);
40823                     break;
40824                 default :
40825                     break;
40826             }
40827             
40828             var el = template.overwrite(td, {"text": text});
40829             
40830             var inner = el.getElementsByTagName("span")[0];
40831             
40832             return {"el": el, "inner": inner};
40833             
40834     }
40835         
40836     
40837 });
40838
40839 /**
40840  * @class Roo.TabPanelItem
40841  * @extends Roo.util.Observable
40842  * Represents an individual item (tab plus body) in a TabPanel.
40843  * @param {Roo.TabPanel} tabPanel The {@link Roo.TabPanel} this TabPanelItem belongs to
40844  * @param {String} id The id of this TabPanelItem
40845  * @param {String} text The text for the tab of this TabPanelItem
40846  * @param {Boolean} closable True to allow this TabPanelItem to be closable (defaults to false)
40847  */
40848 Roo.bootstrap.panel.TabItem = function(config){
40849     /**
40850      * The {@link Roo.TabPanel} this TabPanelItem belongs to
40851      * @type Roo.TabPanel
40852      */
40853     this.tabPanel = config.panel;
40854     /**
40855      * The id for this TabPanelItem
40856      * @type String
40857      */
40858     this.id = config.id;
40859     /** @private */
40860     this.disabled = false;
40861     /** @private */
40862     this.text = config.text;
40863     /** @private */
40864     this.loaded = false;
40865     this.closable = config.closable;
40866
40867     /**
40868      * The body element for this TabPanelItem.
40869      * @type Roo.Element
40870      */
40871     this.bodyEl = Roo.get(this.tabPanel.createItemBody(this.tabPanel.bodyEl.dom, config.id));
40872     this.bodyEl.setVisibilityMode(Roo.Element.VISIBILITY);
40873     this.bodyEl.setStyle("display", "block");
40874     this.bodyEl.setStyle("zoom", "1");
40875     //this.hideAction();
40876
40877     var els = this.tabPanel.createStripElements(this.tabPanel.stripEl.dom, config.text, config.closable, config.tpl);
40878     /** @private */
40879     this.el = Roo.get(els.el);
40880     this.inner = Roo.get(els.inner, true);
40881      this.textEl = Roo.bootstrap.version == 4 ?
40882         this.el : Roo.get(this.el.dom.firstChild, true);
40883
40884     this.pnode = this.linode = Roo.get(els.el.parentNode, true);
40885     this.status_node = Roo.bootstrap.version == 4 ? this.el : this.linode;
40886
40887     
40888 //    this.el.on("mousedown", this.onTabMouseDown, this);
40889     this.el.on("click", this.onTabClick, this);
40890     /** @private */
40891     if(config.closable){
40892         var c = Roo.get(els.close, true);
40893         c.dom.title = this.closeText;
40894         c.addClassOnOver("close-over");
40895         c.on("click", this.closeClick, this);
40896      }
40897
40898     this.addEvents({
40899          /**
40900          * @event activate
40901          * Fires when this tab becomes the active tab.
40902          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40903          * @param {Roo.TabPanelItem} this
40904          */
40905         "activate": true,
40906         /**
40907          * @event beforeclose
40908          * Fires before this tab is closed. To cancel the close, set cancel to true on e (e.cancel = true).
40909          * @param {Roo.TabPanelItem} this
40910          * @param {Object} e Set cancel to true on this object to cancel the close.
40911          */
40912         "beforeclose": true,
40913         /**
40914          * @event close
40915          * Fires when this tab is closed.
40916          * @param {Roo.TabPanelItem} this
40917          */
40918          "close": true,
40919         /**
40920          * @event deactivate
40921          * Fires when this tab is no longer the active tab.
40922          * @param {Roo.TabPanel} tabPanel The parent TabPanel
40923          * @param {Roo.TabPanelItem} this
40924          */
40925          "deactivate" : true
40926     });
40927     this.hidden = false;
40928
40929     Roo.bootstrap.panel.TabItem.superclass.constructor.call(this);
40930 };
40931
40932 Roo.extend(Roo.bootstrap.panel.TabItem, Roo.util.Observable,
40933            {
40934     purgeListeners : function(){
40935        Roo.util.Observable.prototype.purgeListeners.call(this);
40936        this.el.removeAllListeners();
40937     },
40938     /**
40939      * Shows this TabPanelItem -- this <b>does not</b> deactivate the currently active TabPanelItem.
40940      */
40941     show : function(){
40942         this.status_node.addClass("active");
40943         this.showAction();
40944         if(Roo.isOpera){
40945             this.tabPanel.stripWrap.repaint();
40946         }
40947         this.fireEvent("activate", this.tabPanel, this);
40948     },
40949
40950     /**
40951      * Returns true if this tab is the active tab.
40952      * @return {Boolean}
40953      */
40954     isActive : function(){
40955         return this.tabPanel.getActiveTab() == this;
40956     },
40957
40958     /**
40959      * Hides this TabPanelItem -- if you don't activate another TabPanelItem this could look odd.
40960      */
40961     hide : function(){
40962         this.status_node.removeClass("active");
40963         this.hideAction();
40964         this.fireEvent("deactivate", this.tabPanel, this);
40965     },
40966
40967     hideAction : function(){
40968         this.bodyEl.hide();
40969         this.bodyEl.setStyle("position", "absolute");
40970         this.bodyEl.setLeft("-20000px");
40971         this.bodyEl.setTop("-20000px");
40972     },
40973
40974     showAction : function(){
40975         this.bodyEl.setStyle("position", "relative");
40976         this.bodyEl.setTop("");
40977         this.bodyEl.setLeft("");
40978         this.bodyEl.show();
40979     },
40980
40981     /**
40982      * Set the tooltip for the tab.
40983      * @param {String} tooltip The tab's tooltip
40984      */
40985     setTooltip : function(text){
40986         if(Roo.QuickTips && Roo.QuickTips.isEnabled()){
40987             this.textEl.dom.qtip = text;
40988             this.textEl.dom.removeAttribute('title');
40989         }else{
40990             this.textEl.dom.title = text;
40991         }
40992     },
40993
40994     onTabClick : function(e){
40995         e.preventDefault();
40996         this.tabPanel.activate(this.id);
40997     },
40998
40999     onTabMouseDown : function(e){
41000         e.preventDefault();
41001         this.tabPanel.activate(this.id);
41002     },
41003 /*
41004     getWidth : function(){
41005         return this.inner.getWidth();
41006     },
41007
41008     setWidth : function(width){
41009         var iwidth = width - this.linode.getPadding("lr");
41010         this.inner.setWidth(iwidth);
41011         this.textEl.setWidth(iwidth-this.inner.getPadding("lr"));
41012         this.linode.setWidth(width);
41013     },
41014 */
41015     /**
41016      * Show or hide the tab
41017      * @param {Boolean} hidden True to hide or false to show.
41018      */
41019     setHidden : function(hidden){
41020         this.hidden = hidden;
41021         this.linode.setStyle("display", hidden ? "none" : "");
41022     },
41023
41024     /**
41025      * Returns true if this tab is "hidden"
41026      * @return {Boolean}
41027      */
41028     isHidden : function(){
41029         return this.hidden;
41030     },
41031
41032     /**
41033      * Returns the text for this tab
41034      * @return {String}
41035      */
41036     getText : function(){
41037         return this.text;
41038     },
41039     /*
41040     autoSize : function(){
41041         //this.el.beginMeasure();
41042         this.textEl.setWidth(1);
41043         /*
41044          *  #2804 [new] Tabs in Roojs
41045          *  increase the width by 2-4 pixels to prevent the ellipssis showing in chrome
41046          */
41047         //this.setWidth(this.textEl.dom.scrollWidth+this.linode.getPadding("lr")+this.inner.getPadding("lr") + 2);
41048         //this.el.endMeasure();
41049     //},
41050
41051     /**
41052      * Sets the text for the tab (Note: this also sets the tooltip text)
41053      * @param {String} text The tab's text and tooltip
41054      */
41055     setText : function(text){
41056         this.text = text;
41057         this.textEl.update(text);
41058         this.setTooltip(text);
41059         //if(!this.tabPanel.resizeTabs){
41060         //    this.autoSize();
41061         //}
41062     },
41063     /**
41064      * Activates this TabPanelItem -- this <b>does</b> deactivate the currently active TabPanelItem.
41065      */
41066     activate : function(){
41067         this.tabPanel.activate(this.id);
41068     },
41069
41070     /**
41071      * Disables this TabPanelItem -- this does nothing if this is the active TabPanelItem.
41072      */
41073     disable : function(){
41074         if(this.tabPanel.active != this){
41075             this.disabled = true;
41076             this.status_node.addClass("disabled");
41077         }
41078     },
41079
41080     /**
41081      * Enables this TabPanelItem if it was previously disabled.
41082      */
41083     enable : function(){
41084         this.disabled = false;
41085         this.status_node.removeClass("disabled");
41086     },
41087
41088     /**
41089      * Sets the content for this TabPanelItem.
41090      * @param {String} content The content
41091      * @param {Boolean} loadScripts true to look for and load scripts
41092      */
41093     setContent : function(content, loadScripts){
41094         this.bodyEl.update(content, loadScripts);
41095     },
41096
41097     /**
41098      * Gets the {@link Roo.UpdateManager} for the body of this TabPanelItem. Enables you to perform Ajax updates.
41099      * @return {Roo.UpdateManager} The UpdateManager
41100      */
41101     getUpdateManager : function(){
41102         return this.bodyEl.getUpdateManager();
41103     },
41104
41105     /**
41106      * Set a URL to be used to load the content for this TabPanelItem.
41107      * @param {String/Function} url The URL to load the content from, or a function to call to get the URL
41108      * @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)
41109      * @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)
41110      * @return {Roo.UpdateManager} The UpdateManager
41111      */
41112     setUrl : function(url, params, loadOnce){
41113         if(this.refreshDelegate){
41114             this.un('activate', this.refreshDelegate);
41115         }
41116         this.refreshDelegate = this._handleRefresh.createDelegate(this, [url, params, loadOnce]);
41117         this.on("activate", this.refreshDelegate);
41118         return this.bodyEl.getUpdateManager();
41119     },
41120
41121     /** @private */
41122     _handleRefresh : function(url, params, loadOnce){
41123         if(!loadOnce || !this.loaded){
41124             var updater = this.bodyEl.getUpdateManager();
41125             updater.update(url, params, this._setLoaded.createDelegate(this));
41126         }
41127     },
41128
41129     /**
41130      *   Forces a content refresh from the URL specified in the {@link #setUrl} method.
41131      *   Will fail silently if the setUrl method has not been called.
41132      *   This does not activate the panel, just updates its content.
41133      */
41134     refresh : function(){
41135         if(this.refreshDelegate){
41136            this.loaded = false;
41137            this.refreshDelegate();
41138         }
41139     },
41140
41141     /** @private */
41142     _setLoaded : function(){
41143         this.loaded = true;
41144     },
41145
41146     /** @private */
41147     closeClick : function(e){
41148         var o = {};
41149         e.stopEvent();
41150         this.fireEvent("beforeclose", this, o);
41151         if(o.cancel !== true){
41152             this.tabPanel.removeTab(this.id);
41153         }
41154     },
41155     /**
41156      * The text displayed in the tooltip for the close icon.
41157      * @type String
41158      */
41159     closeText : "Close this tab"
41160 });
41161 /**
41162 *    This script refer to:
41163 *    Title: International Telephone Input
41164 *    Author: Jack O'Connor
41165 *    Code version:  v12.1.12
41166 *    Availability: https://github.com/jackocnr/intl-tel-input.git
41167 **/
41168
41169 Roo.bootstrap.PhoneInputData = function() {
41170     var d = [
41171       [
41172         "Afghanistan (‫افغانستان‬‎)",
41173         "af",
41174         "93"
41175       ],
41176       [
41177         "Albania (Shqipëri)",
41178         "al",
41179         "355"
41180       ],
41181       [
41182         "Algeria (‫الجزائر‬‎)",
41183         "dz",
41184         "213"
41185       ],
41186       [
41187         "American Samoa",
41188         "as",
41189         "1684"
41190       ],
41191       [
41192         "Andorra",
41193         "ad",
41194         "376"
41195       ],
41196       [
41197         "Angola",
41198         "ao",
41199         "244"
41200       ],
41201       [
41202         "Anguilla",
41203         "ai",
41204         "1264"
41205       ],
41206       [
41207         "Antigua and Barbuda",
41208         "ag",
41209         "1268"
41210       ],
41211       [
41212         "Argentina",
41213         "ar",
41214         "54"
41215       ],
41216       [
41217         "Armenia (Հայաստան)",
41218         "am",
41219         "374"
41220       ],
41221       [
41222         "Aruba",
41223         "aw",
41224         "297"
41225       ],
41226       [
41227         "Australia",
41228         "au",
41229         "61",
41230         0
41231       ],
41232       [
41233         "Austria (Österreich)",
41234         "at",
41235         "43"
41236       ],
41237       [
41238         "Azerbaijan (Azərbaycan)",
41239         "az",
41240         "994"
41241       ],
41242       [
41243         "Bahamas",
41244         "bs",
41245         "1242"
41246       ],
41247       [
41248         "Bahrain (‫البحرين‬‎)",
41249         "bh",
41250         "973"
41251       ],
41252       [
41253         "Bangladesh (বাংলাদেশ)",
41254         "bd",
41255         "880"
41256       ],
41257       [
41258         "Barbados",
41259         "bb",
41260         "1246"
41261       ],
41262       [
41263         "Belarus (Беларусь)",
41264         "by",
41265         "375"
41266       ],
41267       [
41268         "Belgium (België)",
41269         "be",
41270         "32"
41271       ],
41272       [
41273         "Belize",
41274         "bz",
41275         "501"
41276       ],
41277       [
41278         "Benin (Bénin)",
41279         "bj",
41280         "229"
41281       ],
41282       [
41283         "Bermuda",
41284         "bm",
41285         "1441"
41286       ],
41287       [
41288         "Bhutan (འབྲུག)",
41289         "bt",
41290         "975"
41291       ],
41292       [
41293         "Bolivia",
41294         "bo",
41295         "591"
41296       ],
41297       [
41298         "Bosnia and Herzegovina (Босна и Херцеговина)",
41299         "ba",
41300         "387"
41301       ],
41302       [
41303         "Botswana",
41304         "bw",
41305         "267"
41306       ],
41307       [
41308         "Brazil (Brasil)",
41309         "br",
41310         "55"
41311       ],
41312       [
41313         "British Indian Ocean Territory",
41314         "io",
41315         "246"
41316       ],
41317       [
41318         "British Virgin Islands",
41319         "vg",
41320         "1284"
41321       ],
41322       [
41323         "Brunei",
41324         "bn",
41325         "673"
41326       ],
41327       [
41328         "Bulgaria (България)",
41329         "bg",
41330         "359"
41331       ],
41332       [
41333         "Burkina Faso",
41334         "bf",
41335         "226"
41336       ],
41337       [
41338         "Burundi (Uburundi)",
41339         "bi",
41340         "257"
41341       ],
41342       [
41343         "Cambodia (កម្ពុជា)",
41344         "kh",
41345         "855"
41346       ],
41347       [
41348         "Cameroon (Cameroun)",
41349         "cm",
41350         "237"
41351       ],
41352       [
41353         "Canada",
41354         "ca",
41355         "1",
41356         1,
41357         ["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"]
41358       ],
41359       [
41360         "Cape Verde (Kabu Verdi)",
41361         "cv",
41362         "238"
41363       ],
41364       [
41365         "Caribbean Netherlands",
41366         "bq",
41367         "599",
41368         1
41369       ],
41370       [
41371         "Cayman Islands",
41372         "ky",
41373         "1345"
41374       ],
41375       [
41376         "Central African Republic (République centrafricaine)",
41377         "cf",
41378         "236"
41379       ],
41380       [
41381         "Chad (Tchad)",
41382         "td",
41383         "235"
41384       ],
41385       [
41386         "Chile",
41387         "cl",
41388         "56"
41389       ],
41390       [
41391         "China (中国)",
41392         "cn",
41393         "86"
41394       ],
41395       [
41396         "Christmas Island",
41397         "cx",
41398         "61",
41399         2
41400       ],
41401       [
41402         "Cocos (Keeling) Islands",
41403         "cc",
41404         "61",
41405         1
41406       ],
41407       [
41408         "Colombia",
41409         "co",
41410         "57"
41411       ],
41412       [
41413         "Comoros (‫جزر القمر‬‎)",
41414         "km",
41415         "269"
41416       ],
41417       [
41418         "Congo (DRC) (Jamhuri ya Kidemokrasia ya Kongo)",
41419         "cd",
41420         "243"
41421       ],
41422       [
41423         "Congo (Republic) (Congo-Brazzaville)",
41424         "cg",
41425         "242"
41426       ],
41427       [
41428         "Cook Islands",
41429         "ck",
41430         "682"
41431       ],
41432       [
41433         "Costa Rica",
41434         "cr",
41435         "506"
41436       ],
41437       [
41438         "Côte d’Ivoire",
41439         "ci",
41440         "225"
41441       ],
41442       [
41443         "Croatia (Hrvatska)",
41444         "hr",
41445         "385"
41446       ],
41447       [
41448         "Cuba",
41449         "cu",
41450         "53"
41451       ],
41452       [
41453         "Curaçao",
41454         "cw",
41455         "599",
41456         0
41457       ],
41458       [
41459         "Cyprus (Κύπρος)",
41460         "cy",
41461         "357"
41462       ],
41463       [
41464         "Czech Republic (Česká republika)",
41465         "cz",
41466         "420"
41467       ],
41468       [
41469         "Denmark (Danmark)",
41470         "dk",
41471         "45"
41472       ],
41473       [
41474         "Djibouti",
41475         "dj",
41476         "253"
41477       ],
41478       [
41479         "Dominica",
41480         "dm",
41481         "1767"
41482       ],
41483       [
41484         "Dominican Republic (República Dominicana)",
41485         "do",
41486         "1",
41487         2,
41488         ["809", "829", "849"]
41489       ],
41490       [
41491         "Ecuador",
41492         "ec",
41493         "593"
41494       ],
41495       [
41496         "Egypt (‫مصر‬‎)",
41497         "eg",
41498         "20"
41499       ],
41500       [
41501         "El Salvador",
41502         "sv",
41503         "503"
41504       ],
41505       [
41506         "Equatorial Guinea (Guinea Ecuatorial)",
41507         "gq",
41508         "240"
41509       ],
41510       [
41511         "Eritrea",
41512         "er",
41513         "291"
41514       ],
41515       [
41516         "Estonia (Eesti)",
41517         "ee",
41518         "372"
41519       ],
41520       [
41521         "Ethiopia",
41522         "et",
41523         "251"
41524       ],
41525       [
41526         "Falkland Islands (Islas Malvinas)",
41527         "fk",
41528         "500"
41529       ],
41530       [
41531         "Faroe Islands (Føroyar)",
41532         "fo",
41533         "298"
41534       ],
41535       [
41536         "Fiji",
41537         "fj",
41538         "679"
41539       ],
41540       [
41541         "Finland (Suomi)",
41542         "fi",
41543         "358",
41544         0
41545       ],
41546       [
41547         "France",
41548         "fr",
41549         "33"
41550       ],
41551       [
41552         "French Guiana (Guyane française)",
41553         "gf",
41554         "594"
41555       ],
41556       [
41557         "French Polynesia (Polynésie française)",
41558         "pf",
41559         "689"
41560       ],
41561       [
41562         "Gabon",
41563         "ga",
41564         "241"
41565       ],
41566       [
41567         "Gambia",
41568         "gm",
41569         "220"
41570       ],
41571       [
41572         "Georgia (საქართველო)",
41573         "ge",
41574         "995"
41575       ],
41576       [
41577         "Germany (Deutschland)",
41578         "de",
41579         "49"
41580       ],
41581       [
41582         "Ghana (Gaana)",
41583         "gh",
41584         "233"
41585       ],
41586       [
41587         "Gibraltar",
41588         "gi",
41589         "350"
41590       ],
41591       [
41592         "Greece (Ελλάδα)",
41593         "gr",
41594         "30"
41595       ],
41596       [
41597         "Greenland (Kalaallit Nunaat)",
41598         "gl",
41599         "299"
41600       ],
41601       [
41602         "Grenada",
41603         "gd",
41604         "1473"
41605       ],
41606       [
41607         "Guadeloupe",
41608         "gp",
41609         "590",
41610         0
41611       ],
41612       [
41613         "Guam",
41614         "gu",
41615         "1671"
41616       ],
41617       [
41618         "Guatemala",
41619         "gt",
41620         "502"
41621       ],
41622       [
41623         "Guernsey",
41624         "gg",
41625         "44",
41626         1
41627       ],
41628       [
41629         "Guinea (Guinée)",
41630         "gn",
41631         "224"
41632       ],
41633       [
41634         "Guinea-Bissau (Guiné Bissau)",
41635         "gw",
41636         "245"
41637       ],
41638       [
41639         "Guyana",
41640         "gy",
41641         "592"
41642       ],
41643       [
41644         "Haiti",
41645         "ht",
41646         "509"
41647       ],
41648       [
41649         "Honduras",
41650         "hn",
41651         "504"
41652       ],
41653       [
41654         "Hong Kong (香港)",
41655         "hk",
41656         "852"
41657       ],
41658       [
41659         "Hungary (Magyarország)",
41660         "hu",
41661         "36"
41662       ],
41663       [
41664         "Iceland (Ísland)",
41665         "is",
41666         "354"
41667       ],
41668       [
41669         "India (भारत)",
41670         "in",
41671         "91"
41672       ],
41673       [
41674         "Indonesia",
41675         "id",
41676         "62"
41677       ],
41678       [
41679         "Iran (‫ایران‬‎)",
41680         "ir",
41681         "98"
41682       ],
41683       [
41684         "Iraq (‫العراق‬‎)",
41685         "iq",
41686         "964"
41687       ],
41688       [
41689         "Ireland",
41690         "ie",
41691         "353"
41692       ],
41693       [
41694         "Isle of Man",
41695         "im",
41696         "44",
41697         2
41698       ],
41699       [
41700         "Israel (‫ישראל‬‎)",
41701         "il",
41702         "972"
41703       ],
41704       [
41705         "Italy (Italia)",
41706         "it",
41707         "39",
41708         0
41709       ],
41710       [
41711         "Jamaica",
41712         "jm",
41713         "1876"
41714       ],
41715       [
41716         "Japan (日本)",
41717         "jp",
41718         "81"
41719       ],
41720       [
41721         "Jersey",
41722         "je",
41723         "44",
41724         3
41725       ],
41726       [
41727         "Jordan (‫الأردن‬‎)",
41728         "jo",
41729         "962"
41730       ],
41731       [
41732         "Kazakhstan (Казахстан)",
41733         "kz",
41734         "7",
41735         1
41736       ],
41737       [
41738         "Kenya",
41739         "ke",
41740         "254"
41741       ],
41742       [
41743         "Kiribati",
41744         "ki",
41745         "686"
41746       ],
41747       [
41748         "Kosovo",
41749         "xk",
41750         "383"
41751       ],
41752       [
41753         "Kuwait (‫الكويت‬‎)",
41754         "kw",
41755         "965"
41756       ],
41757       [
41758         "Kyrgyzstan (Кыргызстан)",
41759         "kg",
41760         "996"
41761       ],
41762       [
41763         "Laos (ລາວ)",
41764         "la",
41765         "856"
41766       ],
41767       [
41768         "Latvia (Latvija)",
41769         "lv",
41770         "371"
41771       ],
41772       [
41773         "Lebanon (‫لبنان‬‎)",
41774         "lb",
41775         "961"
41776       ],
41777       [
41778         "Lesotho",
41779         "ls",
41780         "266"
41781       ],
41782       [
41783         "Liberia",
41784         "lr",
41785         "231"
41786       ],
41787       [
41788         "Libya (‫ليبيا‬‎)",
41789         "ly",
41790         "218"
41791       ],
41792       [
41793         "Liechtenstein",
41794         "li",
41795         "423"
41796       ],
41797       [
41798         "Lithuania (Lietuva)",
41799         "lt",
41800         "370"
41801       ],
41802       [
41803         "Luxembourg",
41804         "lu",
41805         "352"
41806       ],
41807       [
41808         "Macau (澳門)",
41809         "mo",
41810         "853"
41811       ],
41812       [
41813         "Macedonia (FYROM) (Македонија)",
41814         "mk",
41815         "389"
41816       ],
41817       [
41818         "Madagascar (Madagasikara)",
41819         "mg",
41820         "261"
41821       ],
41822       [
41823         "Malawi",
41824         "mw",
41825         "265"
41826       ],
41827       [
41828         "Malaysia",
41829         "my",
41830         "60"
41831       ],
41832       [
41833         "Maldives",
41834         "mv",
41835         "960"
41836       ],
41837       [
41838         "Mali",
41839         "ml",
41840         "223"
41841       ],
41842       [
41843         "Malta",
41844         "mt",
41845         "356"
41846       ],
41847       [
41848         "Marshall Islands",
41849         "mh",
41850         "692"
41851       ],
41852       [
41853         "Martinique",
41854         "mq",
41855         "596"
41856       ],
41857       [
41858         "Mauritania (‫موريتانيا‬‎)",
41859         "mr",
41860         "222"
41861       ],
41862       [
41863         "Mauritius (Moris)",
41864         "mu",
41865         "230"
41866       ],
41867       [
41868         "Mayotte",
41869         "yt",
41870         "262",
41871         1
41872       ],
41873       [
41874         "Mexico (México)",
41875         "mx",
41876         "52"
41877       ],
41878       [
41879         "Micronesia",
41880         "fm",
41881         "691"
41882       ],
41883       [
41884         "Moldova (Republica Moldova)",
41885         "md",
41886         "373"
41887       ],
41888       [
41889         "Monaco",
41890         "mc",
41891         "377"
41892       ],
41893       [
41894         "Mongolia (Монгол)",
41895         "mn",
41896         "976"
41897       ],
41898       [
41899         "Montenegro (Crna Gora)",
41900         "me",
41901         "382"
41902       ],
41903       [
41904         "Montserrat",
41905         "ms",
41906         "1664"
41907       ],
41908       [
41909         "Morocco (‫المغرب‬‎)",
41910         "ma",
41911         "212",
41912         0
41913       ],
41914       [
41915         "Mozambique (Moçambique)",
41916         "mz",
41917         "258"
41918       ],
41919       [
41920         "Myanmar (Burma) (မြန်မာ)",
41921         "mm",
41922         "95"
41923       ],
41924       [
41925         "Namibia (Namibië)",
41926         "na",
41927         "264"
41928       ],
41929       [
41930         "Nauru",
41931         "nr",
41932         "674"
41933       ],
41934       [
41935         "Nepal (नेपाल)",
41936         "np",
41937         "977"
41938       ],
41939       [
41940         "Netherlands (Nederland)",
41941         "nl",
41942         "31"
41943       ],
41944       [
41945         "New Caledonia (Nouvelle-Calédonie)",
41946         "nc",
41947         "687"
41948       ],
41949       [
41950         "New Zealand",
41951         "nz",
41952         "64"
41953       ],
41954       [
41955         "Nicaragua",
41956         "ni",
41957         "505"
41958       ],
41959       [
41960         "Niger (Nijar)",
41961         "ne",
41962         "227"
41963       ],
41964       [
41965         "Nigeria",
41966         "ng",
41967         "234"
41968       ],
41969       [
41970         "Niue",
41971         "nu",
41972         "683"
41973       ],
41974       [
41975         "Norfolk Island",
41976         "nf",
41977         "672"
41978       ],
41979       [
41980         "North Korea (조선 민주주의 인민 공화국)",
41981         "kp",
41982         "850"
41983       ],
41984       [
41985         "Northern Mariana Islands",
41986         "mp",
41987         "1670"
41988       ],
41989       [
41990         "Norway (Norge)",
41991         "no",
41992         "47",
41993         0
41994       ],
41995       [
41996         "Oman (‫عُمان‬‎)",
41997         "om",
41998         "968"
41999       ],
42000       [
42001         "Pakistan (‫پاکستان‬‎)",
42002         "pk",
42003         "92"
42004       ],
42005       [
42006         "Palau",
42007         "pw",
42008         "680"
42009       ],
42010       [
42011         "Palestine (‫فلسطين‬‎)",
42012         "ps",
42013         "970"
42014       ],
42015       [
42016         "Panama (Panamá)",
42017         "pa",
42018         "507"
42019       ],
42020       [
42021         "Papua New Guinea",
42022         "pg",
42023         "675"
42024       ],
42025       [
42026         "Paraguay",
42027         "py",
42028         "595"
42029       ],
42030       [
42031         "Peru (Perú)",
42032         "pe",
42033         "51"
42034       ],
42035       [
42036         "Philippines",
42037         "ph",
42038         "63"
42039       ],
42040       [
42041         "Poland (Polska)",
42042         "pl",
42043         "48"
42044       ],
42045       [
42046         "Portugal",
42047         "pt",
42048         "351"
42049       ],
42050       [
42051         "Puerto Rico",
42052         "pr",
42053         "1",
42054         3,
42055         ["787", "939"]
42056       ],
42057       [
42058         "Qatar (‫قطر‬‎)",
42059         "qa",
42060         "974"
42061       ],
42062       [
42063         "Réunion (La Réunion)",
42064         "re",
42065         "262",
42066         0
42067       ],
42068       [
42069         "Romania (România)",
42070         "ro",
42071         "40"
42072       ],
42073       [
42074         "Russia (Россия)",
42075         "ru",
42076         "7",
42077         0
42078       ],
42079       [
42080         "Rwanda",
42081         "rw",
42082         "250"
42083       ],
42084       [
42085         "Saint Barthélemy",
42086         "bl",
42087         "590",
42088         1
42089       ],
42090       [
42091         "Saint Helena",
42092         "sh",
42093         "290"
42094       ],
42095       [
42096         "Saint Kitts and Nevis",
42097         "kn",
42098         "1869"
42099       ],
42100       [
42101         "Saint Lucia",
42102         "lc",
42103         "1758"
42104       ],
42105       [
42106         "Saint Martin (Saint-Martin (partie française))",
42107         "mf",
42108         "590",
42109         2
42110       ],
42111       [
42112         "Saint Pierre and Miquelon (Saint-Pierre-et-Miquelon)",
42113         "pm",
42114         "508"
42115       ],
42116       [
42117         "Saint Vincent and the Grenadines",
42118         "vc",
42119         "1784"
42120       ],
42121       [
42122         "Samoa",
42123         "ws",
42124         "685"
42125       ],
42126       [
42127         "San Marino",
42128         "sm",
42129         "378"
42130       ],
42131       [
42132         "São Tomé and Príncipe (São Tomé e Príncipe)",
42133         "st",
42134         "239"
42135       ],
42136       [
42137         "Saudi Arabia (‫المملكة العربية السعودية‬‎)",
42138         "sa",
42139         "966"
42140       ],
42141       [
42142         "Senegal (Sénégal)",
42143         "sn",
42144         "221"
42145       ],
42146       [
42147         "Serbia (Србија)",
42148         "rs",
42149         "381"
42150       ],
42151       [
42152         "Seychelles",
42153         "sc",
42154         "248"
42155       ],
42156       [
42157         "Sierra Leone",
42158         "sl",
42159         "232"
42160       ],
42161       [
42162         "Singapore",
42163         "sg",
42164         "65"
42165       ],
42166       [
42167         "Sint Maarten",
42168         "sx",
42169         "1721"
42170       ],
42171       [
42172         "Slovakia (Slovensko)",
42173         "sk",
42174         "421"
42175       ],
42176       [
42177         "Slovenia (Slovenija)",
42178         "si",
42179         "386"
42180       ],
42181       [
42182         "Solomon Islands",
42183         "sb",
42184         "677"
42185       ],
42186       [
42187         "Somalia (Soomaaliya)",
42188         "so",
42189         "252"
42190       ],
42191       [
42192         "South Africa",
42193         "za",
42194         "27"
42195       ],
42196       [
42197         "South Korea (대한민국)",
42198         "kr",
42199         "82"
42200       ],
42201       [
42202         "South Sudan (‫جنوب السودان‬‎)",
42203         "ss",
42204         "211"
42205       ],
42206       [
42207         "Spain (España)",
42208         "es",
42209         "34"
42210       ],
42211       [
42212         "Sri Lanka (ශ්‍රී ලංකාව)",
42213         "lk",
42214         "94"
42215       ],
42216       [
42217         "Sudan (‫السودان‬‎)",
42218         "sd",
42219         "249"
42220       ],
42221       [
42222         "Suriname",
42223         "sr",
42224         "597"
42225       ],
42226       [
42227         "Svalbard and Jan Mayen",
42228         "sj",
42229         "47",
42230         1
42231       ],
42232       [
42233         "Swaziland",
42234         "sz",
42235         "268"
42236       ],
42237       [
42238         "Sweden (Sverige)",
42239         "se",
42240         "46"
42241       ],
42242       [
42243         "Switzerland (Schweiz)",
42244         "ch",
42245         "41"
42246       ],
42247       [
42248         "Syria (‫سوريا‬‎)",
42249         "sy",
42250         "963"
42251       ],
42252       [
42253         "Taiwan (台灣)",
42254         "tw",
42255         "886"
42256       ],
42257       [
42258         "Tajikistan",
42259         "tj",
42260         "992"
42261       ],
42262       [
42263         "Tanzania",
42264         "tz",
42265         "255"
42266       ],
42267       [
42268         "Thailand (ไทย)",
42269         "th",
42270         "66"
42271       ],
42272       [
42273         "Timor-Leste",
42274         "tl",
42275         "670"
42276       ],
42277       [
42278         "Togo",
42279         "tg",
42280         "228"
42281       ],
42282       [
42283         "Tokelau",
42284         "tk",
42285         "690"
42286       ],
42287       [
42288         "Tonga",
42289         "to",
42290         "676"
42291       ],
42292       [
42293         "Trinidad and Tobago",
42294         "tt",
42295         "1868"
42296       ],
42297       [
42298         "Tunisia (‫تونس‬‎)",
42299         "tn",
42300         "216"
42301       ],
42302       [
42303         "Turkey (Türkiye)",
42304         "tr",
42305         "90"
42306       ],
42307       [
42308         "Turkmenistan",
42309         "tm",
42310         "993"
42311       ],
42312       [
42313         "Turks and Caicos Islands",
42314         "tc",
42315         "1649"
42316       ],
42317       [
42318         "Tuvalu",
42319         "tv",
42320         "688"
42321       ],
42322       [
42323         "U.S. Virgin Islands",
42324         "vi",
42325         "1340"
42326       ],
42327       [
42328         "Uganda",
42329         "ug",
42330         "256"
42331       ],
42332       [
42333         "Ukraine (Україна)",
42334         "ua",
42335         "380"
42336       ],
42337       [
42338         "United Arab Emirates (‫الإمارات العربية المتحدة‬‎)",
42339         "ae",
42340         "971"
42341       ],
42342       [
42343         "United Kingdom",
42344         "gb",
42345         "44",
42346         0
42347       ],
42348       [
42349         "United States",
42350         "us",
42351         "1",
42352         0
42353       ],
42354       [
42355         "Uruguay",
42356         "uy",
42357         "598"
42358       ],
42359       [
42360         "Uzbekistan (Oʻzbekiston)",
42361         "uz",
42362         "998"
42363       ],
42364       [
42365         "Vanuatu",
42366         "vu",
42367         "678"
42368       ],
42369       [
42370         "Vatican City (Città del Vaticano)",
42371         "va",
42372         "39",
42373         1
42374       ],
42375       [
42376         "Venezuela",
42377         "ve",
42378         "58"
42379       ],
42380       [
42381         "Vietnam (Việt Nam)",
42382         "vn",
42383         "84"
42384       ],
42385       [
42386         "Wallis and Futuna (Wallis-et-Futuna)",
42387         "wf",
42388         "681"
42389       ],
42390       [
42391         "Western Sahara (‫الصحراء الغربية‬‎)",
42392         "eh",
42393         "212",
42394         1
42395       ],
42396       [
42397         "Yemen (‫اليمن‬‎)",
42398         "ye",
42399         "967"
42400       ],
42401       [
42402         "Zambia",
42403         "zm",
42404         "260"
42405       ],
42406       [
42407         "Zimbabwe",
42408         "zw",
42409         "263"
42410       ],
42411       [
42412         "Åland Islands",
42413         "ax",
42414         "358",
42415         1
42416       ]
42417   ];
42418   
42419   return d;
42420 }/**
42421 *    This script refer to:
42422 *    Title: International Telephone Input
42423 *    Author: Jack O'Connor
42424 *    Code version:  v12.1.12
42425 *    Availability: https://github.com/jackocnr/intl-tel-input.git
42426 **/
42427
42428 /**
42429  * @class Roo.bootstrap.PhoneInput
42430  * @extends Roo.bootstrap.TriggerField
42431  * An input with International dial-code selection
42432  
42433  * @cfg {String} defaultDialCode default '+852'
42434  * @cfg {Array} preferedCountries default []
42435   
42436  * @constructor
42437  * Create a new PhoneInput.
42438  * @param {Object} config Configuration options
42439  */
42440
42441 Roo.bootstrap.PhoneInput = function(config) {
42442     Roo.bootstrap.PhoneInput.superclass.constructor.call(this, config);
42443 };
42444
42445 Roo.extend(Roo.bootstrap.PhoneInput, Roo.bootstrap.TriggerField, {
42446         
42447         listWidth: undefined,
42448         
42449         selectedClass: 'active',
42450         
42451         invalidClass : "has-warning",
42452         
42453         validClass: 'has-success',
42454         
42455         allowed: '0123456789',
42456         
42457         max_length: 15,
42458         
42459         /**
42460          * @cfg {String} defaultDialCode The default dial code when initializing the input
42461          */
42462         defaultDialCode: '+852',
42463         
42464         /**
42465          * @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
42466          */
42467         preferedCountries: false,
42468         
42469         getAutoCreate : function()
42470         {
42471             var data = Roo.bootstrap.PhoneInputData();
42472             var align = this.labelAlign || this.parentLabelAlign();
42473             var id = Roo.id();
42474             
42475             this.allCountries = [];
42476             this.dialCodeMapping = [];
42477             
42478             for (var i = 0; i < data.length; i++) {
42479               var c = data[i];
42480               this.allCountries[i] = {
42481                 name: c[0],
42482                 iso2: c[1],
42483                 dialCode: c[2],
42484                 priority: c[3] || 0,
42485                 areaCodes: c[4] || null
42486               };
42487               this.dialCodeMapping[c[2]] = {
42488                   name: c[0],
42489                   iso2: c[1],
42490                   priority: c[3] || 0,
42491                   areaCodes: c[4] || null
42492               };
42493             }
42494             
42495             var cfg = {
42496                 cls: 'form-group',
42497                 cn: []
42498             };
42499             
42500             var input =  {
42501                 tag: 'input',
42502                 id : id,
42503                 // type: 'number', -- do not use number - we get the flaky up/down arrows.
42504                 maxlength: this.max_length,
42505                 cls : 'form-control tel-input',
42506                 autocomplete: 'new-password'
42507             };
42508             
42509             var hiddenInput = {
42510                 tag: 'input',
42511                 type: 'hidden',
42512                 cls: 'hidden-tel-input'
42513             };
42514             
42515             if (this.name) {
42516                 hiddenInput.name = this.name;
42517             }
42518             
42519             if (this.disabled) {
42520                 input.disabled = true;
42521             }
42522             
42523             var flag_container = {
42524                 tag: 'div',
42525                 cls: 'flag-box',
42526                 cn: [
42527                     {
42528                         tag: 'div',
42529                         cls: 'flag'
42530                     },
42531                     {
42532                         tag: 'div',
42533                         cls: 'caret'
42534                     }
42535                 ]
42536             };
42537             
42538             var box = {
42539                 tag: 'div',
42540                 cls: this.hasFeedback ? 'has-feedback' : '',
42541                 cn: [
42542                     hiddenInput,
42543                     input,
42544                     {
42545                         tag: 'input',
42546                         cls: 'dial-code-holder',
42547                         disabled: true
42548                     }
42549                 ]
42550             };
42551             
42552             var container = {
42553                 cls: 'roo-select2-container input-group',
42554                 cn: [
42555                     flag_container,
42556                     box
42557                 ]
42558             };
42559             
42560             if (this.fieldLabel.length) {
42561                 var indicator = {
42562                     tag: 'i',
42563                     tooltip: 'This field is required'
42564                 };
42565                 
42566                 var label = {
42567                     tag: 'label',
42568                     'for':  id,
42569                     cls: 'control-label',
42570                     cn: []
42571                 };
42572                 
42573                 var label_text = {
42574                     tag: 'span',
42575                     html: this.fieldLabel
42576                 };
42577                 
42578                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
42579                 label.cn = [
42580                     indicator,
42581                     label_text
42582                 ];
42583                 
42584                 if(this.indicatorpos == 'right') {
42585                     indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
42586                     label.cn = [
42587                         label_text,
42588                         indicator
42589                     ];
42590                 }
42591                 
42592                 if(align == 'left') {
42593                     container = {
42594                         tag: 'div',
42595                         cn: [
42596                             container
42597                         ]
42598                     };
42599                     
42600                     if(this.labelWidth > 12){
42601                         label.style = "width: " + this.labelWidth + 'px';
42602                     }
42603                     if(this.labelWidth < 13 && this.labelmd == 0){
42604                         this.labelmd = this.labelWidth;
42605                     }
42606                     if(this.labellg > 0){
42607                         label.cls += ' col-lg-' + this.labellg;
42608                         input.cls += ' col-lg-' + (12 - this.labellg);
42609                     }
42610                     if(this.labelmd > 0){
42611                         label.cls += ' col-md-' + this.labelmd;
42612                         container.cls += ' col-md-' + (12 - this.labelmd);
42613                     }
42614                     if(this.labelsm > 0){
42615                         label.cls += ' col-sm-' + this.labelsm;
42616                         container.cls += ' col-sm-' + (12 - this.labelsm);
42617                     }
42618                     if(this.labelxs > 0){
42619                         label.cls += ' col-xs-' + this.labelxs;
42620                         container.cls += ' col-xs-' + (12 - this.labelxs);
42621                     }
42622                 }
42623             }
42624             
42625             cfg.cn = [
42626                 label,
42627                 container
42628             ];
42629             
42630             var settings = this;
42631             
42632             ['xs','sm','md','lg'].map(function(size){
42633                 if (settings[size]) {
42634                     cfg.cls += ' col-' + size + '-' + settings[size];
42635                 }
42636             });
42637             
42638             this.store = new Roo.data.Store({
42639                 proxy : new Roo.data.MemoryProxy({}),
42640                 reader : new Roo.data.JsonReader({
42641                     fields : [
42642                         {
42643                             'name' : 'name',
42644                             'type' : 'string'
42645                         },
42646                         {
42647                             'name' : 'iso2',
42648                             'type' : 'string'
42649                         },
42650                         {
42651                             'name' : 'dialCode',
42652                             'type' : 'string'
42653                         },
42654                         {
42655                             'name' : 'priority',
42656                             'type' : 'string'
42657                         },
42658                         {
42659                             'name' : 'areaCodes',
42660                             'type' : 'string'
42661                         }
42662                     ]
42663                 })
42664             });
42665             
42666             if(!this.preferedCountries) {
42667                 this.preferedCountries = [
42668                     'hk',
42669                     'gb',
42670                     'us'
42671                 ];
42672             }
42673             
42674             var p = this.preferedCountries.reverse();
42675             
42676             if(p) {
42677                 for (var i = 0; i < p.length; i++) {
42678                     for (var j = 0; j < this.allCountries.length; j++) {
42679                         if(this.allCountries[j].iso2 == p[i]) {
42680                             var t = this.allCountries[j];
42681                             this.allCountries.splice(j,1);
42682                             this.allCountries.unshift(t);
42683                         }
42684                     } 
42685                 }
42686             }
42687             
42688             this.store.proxy.data = {
42689                 success: true,
42690                 data: this.allCountries
42691             };
42692             
42693             return cfg;
42694         },
42695         
42696         initEvents : function()
42697         {
42698             this.createList();
42699             Roo.bootstrap.PhoneInput.superclass.initEvents.call(this);
42700             
42701             this.indicator = this.indicatorEl();
42702             this.flag = this.flagEl();
42703             this.dialCodeHolder = this.dialCodeHolderEl();
42704             
42705             this.trigger = this.el.select('div.flag-box',true).first();
42706             this.trigger.on("click", this.onTriggerClick, this, {preventDefault:true});
42707             
42708             var _this = this;
42709             
42710             (function(){
42711                 var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
42712                 _this.list.setWidth(lw);
42713             }).defer(100);
42714             
42715             this.list.on('mouseover', this.onViewOver, this);
42716             this.list.on('mousemove', this.onViewMove, this);
42717             this.inputEl().on("keyup", this.onKeyUp, this);
42718             this.inputEl().on("keypress", this.onKeyPress, this);
42719             
42720             this.tpl = '<li><a href="#"><div class="flag {iso2}"></div>{name} <span class="dial-code">+{dialCode}</span></a></li>';
42721
42722             this.view = new Roo.View(this.list, this.tpl, {
42723                 singleSelect:true, store: this.store, selectedClass: this.selectedClass
42724             });
42725             
42726             this.view.on('click', this.onViewClick, this);
42727             this.setValue(this.defaultDialCode);
42728         },
42729         
42730         onTriggerClick : function(e)
42731         {
42732             Roo.log('trigger click');
42733             if(this.disabled){
42734                 return;
42735             }
42736             
42737             if(this.isExpanded()){
42738                 this.collapse();
42739                 this.hasFocus = false;
42740             }else {
42741                 this.store.load({});
42742                 this.hasFocus = true;
42743                 this.expand();
42744             }
42745         },
42746         
42747         isExpanded : function()
42748         {
42749             return this.list.isVisible();
42750         },
42751         
42752         collapse : function()
42753         {
42754             if(!this.isExpanded()){
42755                 return;
42756             }
42757             this.list.hide();
42758             Roo.get(document).un('mousedown', this.collapseIf, this);
42759             Roo.get(document).un('mousewheel', this.collapseIf, this);
42760             this.fireEvent('collapse', this);
42761             this.validate();
42762         },
42763         
42764         expand : function()
42765         {
42766             Roo.log('expand');
42767
42768             if(this.isExpanded() || !this.hasFocus){
42769                 return;
42770             }
42771             
42772             var lw = this.listWidth || Math.max(this.inputEl().getWidth(), this.minListWidth);
42773             this.list.setWidth(lw);
42774             
42775             this.list.show();
42776             this.restrictHeight();
42777             
42778             Roo.get(document).on('mousedown', this.collapseIf, this);
42779             Roo.get(document).on('mousewheel', this.collapseIf, this);
42780             
42781             this.fireEvent('expand', this);
42782         },
42783         
42784         restrictHeight : function()
42785         {
42786             this.list.alignTo(this.inputEl(), this.listAlign);
42787             this.list.alignTo(this.inputEl(), this.listAlign);
42788         },
42789         
42790         onViewOver : function(e, t)
42791         {
42792             if(this.inKeyMode){
42793                 return;
42794             }
42795             var item = this.view.findItemFromChild(t);
42796             
42797             if(item){
42798                 var index = this.view.indexOf(item);
42799                 this.select(index, false);
42800             }
42801         },
42802
42803         // private
42804         onViewClick : function(view, doFocus, el, e)
42805         {
42806             var index = this.view.getSelectedIndexes()[0];
42807             
42808             var r = this.store.getAt(index);
42809             
42810             if(r){
42811                 this.onSelect(r, index);
42812             }
42813             if(doFocus !== false && !this.blockFocus){
42814                 this.inputEl().focus();
42815             }
42816         },
42817         
42818         onViewMove : function(e, t)
42819         {
42820             this.inKeyMode = false;
42821         },
42822         
42823         select : function(index, scrollIntoView)
42824         {
42825             this.selectedIndex = index;
42826             this.view.select(index);
42827             if(scrollIntoView !== false){
42828                 var el = this.view.getNode(index);
42829                 if(el){
42830                     this.list.scrollChildIntoView(el, false);
42831                 }
42832             }
42833         },
42834         
42835         createList : function()
42836         {
42837             this.list = Roo.get(document.body).createChild({
42838                 tag: 'ul',
42839                 cls: 'typeahead typeahead-long dropdown-menu tel-list',
42840                 style: 'display:none'
42841             });
42842             
42843             this.list.setVisibilityMode(Roo.Element.DISPLAY).originalDisplay = 'block';
42844         },
42845         
42846         collapseIf : function(e)
42847         {
42848             var in_combo  = e.within(this.el);
42849             var in_list =  e.within(this.list);
42850             var is_list = (Roo.get(e.getTarget()).id == this.list.id) ? true : false;
42851             
42852             if (in_combo || in_list || is_list) {
42853                 return;
42854             }
42855             this.collapse();
42856         },
42857         
42858         onSelect : function(record, index)
42859         {
42860             if(this.fireEvent('beforeselect', this, record, index) !== false){
42861                 
42862                 this.setFlagClass(record.data.iso2);
42863                 this.setDialCode(record.data.dialCode);
42864                 this.hasFocus = false;
42865                 this.collapse();
42866                 this.fireEvent('select', this, record, index);
42867             }
42868         },
42869         
42870         flagEl : function()
42871         {
42872             var flag = this.el.select('div.flag',true).first();
42873             if(!flag){
42874                 return false;
42875             }
42876             return flag;
42877         },
42878         
42879         dialCodeHolderEl : function()
42880         {
42881             var d = this.el.select('input.dial-code-holder',true).first();
42882             if(!d){
42883                 return false;
42884             }
42885             return d;
42886         },
42887         
42888         setDialCode : function(v)
42889         {
42890             this.dialCodeHolder.dom.value = '+'+v;
42891         },
42892         
42893         setFlagClass : function(n)
42894         {
42895             this.flag.dom.className = 'flag '+n;
42896         },
42897         
42898         getValue : function()
42899         {
42900             var v = this.inputEl().getValue();
42901             if(this.dialCodeHolder) {
42902                 v = this.dialCodeHolder.dom.value+this.inputEl().getValue();
42903             }
42904             return v;
42905         },
42906         
42907         setValue : function(v)
42908         {
42909             var d = this.getDialCode(v);
42910             
42911             //invalid dial code
42912             if(v.length == 0 || !d || d.length == 0) {
42913                 if(this.rendered){
42914                     this.inputEl().dom.value = (v === null || v === undefined ? '' : v);
42915                     this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
42916                 }
42917                 return;
42918             }
42919             
42920             //valid dial code
42921             this.setFlagClass(this.dialCodeMapping[d].iso2);
42922             this.setDialCode(d);
42923             this.inputEl().dom.value = v.replace('+'+d,'');
42924             this.hiddenEl().dom.value = this.getValue();
42925             
42926             this.validate();
42927         },
42928         
42929         getDialCode : function(v)
42930         {
42931             v = v ||  '';
42932             
42933             if (v.length == 0) {
42934                 return this.dialCodeHolder.dom.value;
42935             }
42936             
42937             var dialCode = "";
42938             if (v.charAt(0) != "+") {
42939                 return false;
42940             }
42941             var numericChars = "";
42942             for (var i = 1; i < v.length; i++) {
42943               var c = v.charAt(i);
42944               if (!isNaN(c)) {
42945                 numericChars += c;
42946                 if (this.dialCodeMapping[numericChars]) {
42947                   dialCode = v.substr(1, i);
42948                 }
42949                 if (numericChars.length == 4) {
42950                   break;
42951                 }
42952               }
42953             }
42954             return dialCode;
42955         },
42956         
42957         reset : function()
42958         {
42959             this.setValue(this.defaultDialCode);
42960             this.validate();
42961         },
42962         
42963         hiddenEl : function()
42964         {
42965             return this.el.select('input.hidden-tel-input',true).first();
42966         },
42967         
42968         // after setting val
42969         onKeyUp : function(e){
42970             this.setValue(this.getValue());
42971         },
42972         
42973         onKeyPress : function(e){
42974             if(this.allowed.indexOf(String.fromCharCode(e.getCharCode())) === -1){
42975                 e.stopEvent();
42976             }
42977         }
42978         
42979 });
42980 /**
42981  * @class Roo.bootstrap.MoneyField
42982  * @extends Roo.bootstrap.ComboBox
42983  * Bootstrap MoneyField class
42984  * 
42985  * @constructor
42986  * Create a new MoneyField.
42987  * @param {Object} config Configuration options
42988  */
42989
42990 Roo.bootstrap.MoneyField = function(config) {
42991     
42992     Roo.bootstrap.MoneyField.superclass.constructor.call(this, config);
42993     
42994 };
42995
42996 Roo.extend(Roo.bootstrap.MoneyField, Roo.bootstrap.ComboBox, {
42997     
42998     /**
42999      * @cfg {Boolean} allowDecimals False to disallow decimal values (defaults to true)
43000      */
43001     allowDecimals : true,
43002     /**
43003      * @cfg {String} decimalSeparator Character(s) to allow as the decimal separator (defaults to '.')
43004      */
43005     decimalSeparator : ".",
43006     /**
43007      * @cfg {Number} decimalPrecision The maximum precision to display after the decimal separator (defaults to 2)
43008      */
43009     decimalPrecision : 0,
43010     /**
43011      * @cfg {Boolean} allowNegative False to prevent entering a negative sign (defaults to true)
43012      */
43013     allowNegative : true,
43014     /**
43015      * @cfg {Boolean} allowZero False to blank out if the user enters '0' (defaults to true)
43016      */
43017     allowZero: true,
43018     /**
43019      * @cfg {Number} minValue The minimum allowed value (defaults to Number.NEGATIVE_INFINITY)
43020      */
43021     minValue : Number.NEGATIVE_INFINITY,
43022     /**
43023      * @cfg {Number} maxValue The maximum allowed value (defaults to Number.MAX_VALUE)
43024      */
43025     maxValue : Number.MAX_VALUE,
43026     /**
43027      * @cfg {String} minText Error text to display if the minimum value validation fails (defaults to "The minimum value for this field is {minValue}")
43028      */
43029     minText : "The minimum value for this field is {0}",
43030     /**
43031      * @cfg {String} maxText Error text to display if the maximum value validation fails (defaults to "The maximum value for this field is {maxValue}")
43032      */
43033     maxText : "The maximum value for this field is {0}",
43034     /**
43035      * @cfg {String} nanText Error text to display if the value is not a valid number.  For example, this can happen
43036      * if a valid character like '.' or '-' is left in the field with no number (defaults to "{value} is not a valid number")
43037      */
43038     nanText : "{0} is not a valid number",
43039     /**
43040      * @cfg {Boolean} castInt (true|false) cast int if true (defalut true)
43041      */
43042     castInt : true,
43043     /**
43044      * @cfg {String} defaults currency of the MoneyField
43045      * value should be in lkey
43046      */
43047     defaultCurrency : false,
43048     /**
43049      * @cfg {String} thousandsDelimiter Symbol of thousandsDelimiter
43050      */
43051     thousandsDelimiter : false,
43052     /**
43053      * @cfg {Number} max_length Maximum input field length allowed (defaults to Number.MAX_VALUE)
43054      */
43055     max_length: false,
43056     
43057     inputlg : 9,
43058     inputmd : 9,
43059     inputsm : 9,
43060     inputxs : 6,
43061     
43062     store : false,
43063     
43064     getAutoCreate : function()
43065     {
43066         var align = this.labelAlign || this.parentLabelAlign();
43067         
43068         var id = Roo.id();
43069
43070         var cfg = {
43071             cls: 'form-group',
43072             cn: []
43073         };
43074
43075         var input =  {
43076             tag: 'input',
43077             id : id,
43078             cls : 'form-control roo-money-amount-input',
43079             autocomplete: 'new-password'
43080         };
43081         
43082         var hiddenInput = {
43083             tag: 'input',
43084             type: 'hidden',
43085             id: Roo.id(),
43086             cls: 'hidden-number-input'
43087         };
43088         
43089         if(this.max_length) {
43090             input.maxlength = this.max_length; 
43091         }
43092         
43093         if (this.name) {
43094             hiddenInput.name = this.name;
43095         }
43096
43097         if (this.disabled) {
43098             input.disabled = true;
43099         }
43100
43101         var clg = 12 - this.inputlg;
43102         var cmd = 12 - this.inputmd;
43103         var csm = 12 - this.inputsm;
43104         var cxs = 12 - this.inputxs;
43105         
43106         var container = {
43107             tag : 'div',
43108             cls : 'row roo-money-field',
43109             cn : [
43110                 {
43111                     tag : 'div',
43112                     cls : 'roo-money-currency column col-lg-' + clg + ' col-md-' + cmd + ' col-sm-' + csm + ' col-xs-' + cxs,
43113                     cn : [
43114                         {
43115                             tag : 'div',
43116                             cls: 'roo-select2-container input-group',
43117                             cn: [
43118                                 {
43119                                     tag : 'input',
43120                                     cls : 'form-control roo-money-currency-input',
43121                                     autocomplete: 'new-password',
43122                                     readOnly : 1,
43123                                     name : this.currencyName
43124                                 },
43125                                 {
43126                                     tag :'span',
43127                                     cls : 'input-group-addon',
43128                                     cn : [
43129                                         {
43130                                             tag: 'span',
43131                                             cls: 'caret'
43132                                         }
43133                                     ]
43134                                 }
43135                             ]
43136                         }
43137                     ]
43138                 },
43139                 {
43140                     tag : 'div',
43141                     cls : 'roo-money-amount column col-lg-' + this.inputlg + ' col-md-' + this.inputmd + ' col-sm-' + this.inputsm + ' col-xs-' + this.inputxs,
43142                     cn : [
43143                         {
43144                             tag: 'div',
43145                             cls: this.hasFeedback ? 'has-feedback' : '',
43146                             cn: [
43147                                 input
43148                             ]
43149                         }
43150                     ]
43151                 }
43152             ]
43153             
43154         };
43155         
43156         if (this.fieldLabel.length) {
43157             var indicator = {
43158                 tag: 'i',
43159                 tooltip: 'This field is required'
43160             };
43161
43162             var label = {
43163                 tag: 'label',
43164                 'for':  id,
43165                 cls: 'control-label',
43166                 cn: []
43167             };
43168
43169             var label_text = {
43170                 tag: 'span',
43171                 html: this.fieldLabel
43172             };
43173
43174             indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star left-indicator';
43175             label.cn = [
43176                 indicator,
43177                 label_text
43178             ];
43179
43180             if(this.indicatorpos == 'right') {
43181                 indicator.cls = 'roo-required-indicator text-danger fa fa-lg fa-star right-indicator';
43182                 label.cn = [
43183                     label_text,
43184                     indicator
43185                 ];
43186             }
43187
43188             if(align == 'left') {
43189                 container = {
43190                     tag: 'div',
43191                     cn: [
43192                         container
43193                     ]
43194                 };
43195
43196                 if(this.labelWidth > 12){
43197                     label.style = "width: " + this.labelWidth + 'px';
43198                 }
43199                 if(this.labelWidth < 13 && this.labelmd == 0){
43200                     this.labelmd = this.labelWidth;
43201                 }
43202                 if(this.labellg > 0){
43203                     label.cls += ' col-lg-' + this.labellg;
43204                     input.cls += ' col-lg-' + (12 - this.labellg);
43205                 }
43206                 if(this.labelmd > 0){
43207                     label.cls += ' col-md-' + this.labelmd;
43208                     container.cls += ' col-md-' + (12 - this.labelmd);
43209                 }
43210                 if(this.labelsm > 0){
43211                     label.cls += ' col-sm-' + this.labelsm;
43212                     container.cls += ' col-sm-' + (12 - this.labelsm);
43213                 }
43214                 if(this.labelxs > 0){
43215                     label.cls += ' col-xs-' + this.labelxs;
43216                     container.cls += ' col-xs-' + (12 - this.labelxs);
43217                 }
43218             }
43219         }
43220
43221         cfg.cn = [
43222             label,
43223             container,
43224             hiddenInput
43225         ];
43226         
43227         var settings = this;
43228
43229         ['xs','sm','md','lg'].map(function(size){
43230             if (settings[size]) {
43231                 cfg.cls += ' col-' + size + '-' + settings[size];
43232             }
43233         });
43234         
43235         return cfg;
43236     },
43237     
43238     initEvents : function()
43239     {
43240         this.indicator = this.indicatorEl();
43241         
43242         this.initCurrencyEvent();
43243         
43244         this.initNumberEvent();
43245     },
43246     
43247     initCurrencyEvent : function()
43248     {
43249         if (!this.store) {
43250             throw "can not find store for combo";
43251         }
43252         
43253         this.store = Roo.factory(this.store, Roo.data);
43254         this.store.parent = this;
43255         
43256         this.createList();
43257         
43258         this.triggerEl = this.el.select('.input-group-addon', true).first();
43259         
43260         this.triggerEl.on("click", this.onTriggerClick, this, { preventDefault : true });
43261         
43262         var _this = this;
43263         
43264         (function(){
43265             var lw = _this.listWidth || Math.max(_this.inputEl().getWidth(), _this.minListWidth);
43266             _this.list.setWidth(lw);
43267         }).defer(100);
43268         
43269         this.list.on('mouseover', this.onViewOver, this);
43270         this.list.on('mousemove', this.onViewMove, this);
43271         this.list.on('scroll', this.onViewScroll, this);
43272         
43273         if(!this.tpl){
43274             this.tpl = '<li><a href="#">{' + this.currencyField + '}</a></li>';
43275         }
43276         
43277         this.view = new Roo.View(this.list, this.tpl, {
43278             singleSelect:true, store: this.store, selectedClass: this.selectedClass
43279         });
43280         
43281         this.view.on('click', this.onViewClick, this);
43282         
43283         this.store.on('beforeload', this.onBeforeLoad, this);
43284         this.store.on('load', this.onLoad, this);
43285         this.store.on('loadexception', this.onLoadException, this);
43286         
43287         this.keyNav = new Roo.KeyNav(this.currencyEl(), {
43288             "up" : function(e){
43289                 this.inKeyMode = true;
43290                 this.selectPrev();
43291             },
43292
43293             "down" : function(e){
43294                 if(!this.isExpanded()){
43295                     this.onTriggerClick();
43296                 }else{
43297                     this.inKeyMode = true;
43298                     this.selectNext();
43299                 }
43300             },
43301
43302             "enter" : function(e){
43303                 this.collapse();
43304                 
43305                 if(this.fireEvent("specialkey", this, e)){
43306                     this.onViewClick(false);
43307                 }
43308                 
43309                 return true;
43310             },
43311
43312             "esc" : function(e){
43313                 this.collapse();
43314             },
43315
43316             "tab" : function(e){
43317                 this.collapse();
43318                 
43319                 if(this.fireEvent("specialkey", this, e)){
43320                     this.onViewClick(false);
43321                 }
43322                 
43323                 return true;
43324             },
43325
43326             scope : this,
43327
43328             doRelay : function(foo, bar, hname){
43329                 if(hname == 'down' || this.scope.isExpanded()){
43330                    return Roo.KeyNav.prototype.doRelay.apply(this, arguments);
43331                 }
43332                 return true;
43333             },
43334
43335             forceKeyDown: true
43336         });
43337         
43338         this.currencyEl().on("click", this.onTriggerClick, this, { preventDefault : true });
43339         
43340     },
43341     
43342     initNumberEvent : function(e)
43343     {
43344         this.inputEl().on("keydown" , this.fireKey,  this);
43345         this.inputEl().on("focus", this.onFocus,  this);
43346         this.inputEl().on("blur", this.onBlur,  this);
43347         
43348         this.inputEl().relayEvent('keyup', this);
43349         
43350         if(this.indicator){
43351             this.indicator.addClass('invisible');
43352         }
43353  
43354         this.originalValue = this.getValue();
43355         
43356         if(this.validationEvent == 'keyup'){
43357             this.validationTask = new Roo.util.DelayedTask(this.validate, this);
43358             this.inputEl().on('keyup', this.filterValidation, this);
43359         }
43360         else if(this.validationEvent !== false){
43361             this.inputEl().on(this.validationEvent, this.validate, this, {buffer: this.validationDelay});
43362         }
43363         
43364         if(this.selectOnFocus){
43365             this.on("focus", this.preFocus, this);
43366             
43367         }
43368         if(this.maskRe || (this.vtype && this.disableKeyFilter !== true && (this.maskRe = Roo.form.VTypes[this.vtype+'Mask']))){
43369             this.inputEl().on("keypress", this.filterKeys, this);
43370         } else {
43371             this.inputEl().relayEvent('keypress', this);
43372         }
43373         
43374         var allowed = "0123456789";
43375         
43376         if(this.allowDecimals){
43377             allowed += this.decimalSeparator;
43378         }
43379         
43380         if(this.allowNegative){
43381             allowed += "-";
43382         }
43383         
43384         if(this.thousandsDelimiter) {
43385             allowed += ",";
43386         }
43387         
43388         this.stripCharsRe = new RegExp('[^'+allowed+']', 'gi');
43389         
43390         var keyPress = function(e){
43391             
43392             var k = e.getKey();
43393             
43394             var c = e.getCharCode();
43395             
43396             if(
43397                     (String.fromCharCode(c) == '.' || String.fromCharCode(c) == '-') &&
43398                     allowed.indexOf(String.fromCharCode(c)) === -1
43399             ){
43400                 e.stopEvent();
43401                 return;
43402             }
43403             
43404             if(!Roo.isIE && (e.isSpecialKey() || k == e.BACKSPACE || k == e.DELETE)){
43405                 return;
43406             }
43407             
43408             if(allowed.indexOf(String.fromCharCode(c)) === -1){
43409                 e.stopEvent();
43410             }
43411         };
43412         
43413         this.inputEl().on("keypress", keyPress, this);
43414         
43415     },
43416     
43417     onTriggerClick : function(e)
43418     {   
43419         if(this.disabled){
43420             return;
43421         }
43422         
43423         this.page = 0;
43424         this.loadNext = false;
43425         
43426         if(this.isExpanded()){
43427             this.collapse();
43428             return;
43429         }
43430         
43431         this.hasFocus = true;
43432         
43433         if(this.triggerAction == 'all') {
43434             this.doQuery(this.allQuery, true);
43435             return;
43436         }
43437         
43438         this.doQuery(this.getRawValue());
43439     },
43440     
43441     getCurrency : function()
43442     {   
43443         var v = this.currencyEl().getValue();
43444         
43445         return v;
43446     },
43447     
43448     restrictHeight : function()
43449     {
43450         this.list.alignTo(this.currencyEl(), this.listAlign);
43451         this.list.alignTo(this.currencyEl(), this.listAlign);
43452     },
43453     
43454     onViewClick : function(view, doFocus, el, e)
43455     {
43456         var index = this.view.getSelectedIndexes()[0];
43457         
43458         var r = this.store.getAt(index);
43459         
43460         if(r){
43461             this.onSelect(r, index);
43462         }
43463     },
43464     
43465     onSelect : function(record, index){
43466         
43467         if(this.fireEvent('beforeselect', this, record, index) !== false){
43468         
43469             this.setFromCurrencyData(index > -1 ? record.data : false);
43470             
43471             this.collapse();
43472             
43473             this.fireEvent('select', this, record, index);
43474         }
43475     },
43476     
43477     setFromCurrencyData : function(o)
43478     {
43479         var currency = '';
43480         
43481         this.lastCurrency = o;
43482         
43483         if (this.currencyField) {
43484             currency = !o || typeof(o[this.currencyField]) == 'undefined' ? '' : o[this.currencyField];
43485         } else {
43486             Roo.log('no  currencyField value set for '+ (this.name ? this.name : this.id));
43487         }
43488         
43489         this.lastSelectionText = currency;
43490         
43491         //setting default currency
43492         if(o[this.currencyField] * 1 == 0 && this.defaultCurrency) {
43493             this.setCurrency(this.defaultCurrency);
43494             return;
43495         }
43496         
43497         this.setCurrency(currency);
43498     },
43499     
43500     setFromData : function(o)
43501     {
43502         var c = {};
43503         
43504         c[this.currencyField] = !o || typeof(o[this.currencyName]) == 'undefined' ? '' : o[this.currencyName];
43505         
43506         this.setFromCurrencyData(c);
43507         
43508         var value = '';
43509         
43510         if (this.name) {
43511             value = !o || typeof(o[this.name]) == 'undefined' ? '' : o[this.name];
43512         } else {
43513             Roo.log('no value set for '+ (this.name ? this.name : this.id));
43514         }
43515         
43516         this.setValue(value);
43517         
43518     },
43519     
43520     setCurrency : function(v)
43521     {   
43522         this.currencyValue = v;
43523         
43524         if(this.rendered){
43525             this.currencyEl().dom.value = (v === null || v === undefined ? '' : v);
43526             this.validate();
43527         }
43528     },
43529     
43530     setValue : function(v)
43531     {
43532         v = String(this.fixPrecision(v)).replace(".", this.decimalSeparator);
43533         
43534         this.value = v;
43535         
43536         if(this.rendered){
43537             
43538             this.hiddenEl().dom.value = (v === null || v === undefined ? '' : v);
43539             
43540             this.inputEl().dom.value = (v == '') ? '' :
43541                 Roo.util.Format.number(v, this.decimalPrecision, this.thousandsDelimiter || '');
43542             
43543             if(!this.allowZero && v === '0') {
43544                 this.hiddenEl().dom.value = '';
43545                 this.inputEl().dom.value = '';
43546             }
43547             
43548             this.validate();
43549         }
43550     },
43551     
43552     getRawValue : function()
43553     {
43554         var v = this.inputEl().getValue();
43555         
43556         return v;
43557     },
43558     
43559     getValue : function()
43560     {
43561         return this.fixPrecision(this.parseValue(this.getRawValue()));
43562     },
43563     
43564     parseValue : function(value)
43565     {
43566         if(this.thousandsDelimiter) {
43567             value += "";
43568             r = new RegExp(",", "g");
43569             value = value.replace(r, "");
43570         }
43571         
43572         value = parseFloat(String(value).replace(this.decimalSeparator, "."));
43573         return isNaN(value) ? '' : value;
43574         
43575     },
43576     
43577     fixPrecision : function(value)
43578     {
43579         if(this.thousandsDelimiter) {
43580             value += "";
43581             r = new RegExp(",", "g");
43582             value = value.replace(r, "");
43583         }
43584         
43585         var nan = isNaN(value);
43586         
43587         if(!this.allowDecimals || this.decimalPrecision == -1 || nan || !value){
43588             return nan ? '' : value;
43589         }
43590         return parseFloat(value).toFixed(this.decimalPrecision);
43591     },
43592     
43593     decimalPrecisionFcn : function(v)
43594     {
43595         return Math.floor(v);
43596     },
43597     
43598     validateValue : function(value)
43599     {
43600         if(!Roo.bootstrap.MoneyField.superclass.validateValue.call(this, value)){
43601             return false;
43602         }
43603         
43604         var num = this.parseValue(value);
43605         
43606         if(isNaN(num)){
43607             this.markInvalid(String.format(this.nanText, value));
43608             return false;
43609         }
43610         
43611         if(num < this.minValue){
43612             this.markInvalid(String.format(this.minText, this.minValue));
43613             return false;
43614         }
43615         
43616         if(num > this.maxValue){
43617             this.markInvalid(String.format(this.maxText, this.maxValue));
43618             return false;
43619         }
43620         
43621         return true;
43622     },
43623     
43624     validate : function()
43625     {
43626         if(this.disabled || this.allowBlank){
43627             this.markValid();
43628             return true;
43629         }
43630         
43631         var currency = this.getCurrency();
43632         
43633         if(this.validateValue(this.getRawValue()) && currency.length){
43634             this.markValid();
43635             return true;
43636         }
43637         
43638         this.markInvalid();
43639         return false;
43640     },
43641     
43642     getName: function()
43643     {
43644         return this.name;
43645     },
43646     
43647     beforeBlur : function()
43648     {
43649         if(!this.castInt){
43650             return;
43651         }
43652         
43653         var v = this.parseValue(this.getRawValue());
43654         
43655         if(v || v == 0){
43656             this.setValue(v);
43657         }
43658     },
43659     
43660     onBlur : function()
43661     {
43662         this.beforeBlur();
43663         
43664         if(!Roo.isOpera && this.focusClass){ // don't touch in Opera
43665             //this.el.removeClass(this.focusClass);
43666         }
43667         
43668         this.hasFocus = false;
43669         
43670         if(this.validationEvent !== false && this.validateOnBlur && this.validationEvent != "blur"){
43671             this.validate();
43672         }
43673         
43674         var v = this.getValue();
43675         
43676         if(String(v) !== String(this.startValue)){
43677             this.fireEvent('change', this, v, this.startValue);
43678         }
43679         
43680         this.fireEvent("blur", this);
43681     },
43682     
43683     inputEl : function()
43684     {
43685         return this.el.select('.roo-money-amount-input', true).first();
43686     },
43687     
43688     currencyEl : function()
43689     {
43690         return this.el.select('.roo-money-currency-input', true).first();
43691     },
43692     
43693     hiddenEl : function()
43694     {
43695         return this.el.select('input.hidden-number-input',true).first();
43696     }
43697     
43698 });/**
43699  * @class Roo.bootstrap.BezierSignature
43700  * @extends Roo.bootstrap.Component
43701  * Bootstrap BezierSignature class
43702  * This script refer to:
43703  *    Title: Signature Pad
43704  *    Author: szimek
43705  *    Availability: https://github.com/szimek/signature_pad
43706  *
43707  * @constructor
43708  * Create a new BezierSignature
43709  * @param {Object} config The config object
43710  */
43711
43712 Roo.bootstrap.BezierSignature = function(config){
43713     Roo.bootstrap.BezierSignature.superclass.constructor.call(this, config);
43714     this.addEvents({
43715         "resize" : true
43716     });
43717 };
43718
43719 Roo.extend(Roo.bootstrap.BezierSignature, Roo.bootstrap.Component,
43720 {
43721      
43722     curve_data: [],
43723     
43724     is_empty: true,
43725     
43726     mouse_btn_down: true,
43727     
43728     /**
43729      * @cfg {int} canvas height
43730      */
43731     canvas_height: '200px',
43732     
43733     /**
43734      * @cfg {float|function} Radius of a single dot.
43735      */ 
43736     dot_size: false,
43737     
43738     /**
43739      * @cfg {float} Minimum width of a line. Defaults to 0.5.
43740      */
43741     min_width: 0.5,
43742     
43743     /**
43744      * @cfg {float} Maximum width of a line. Defaults to 2.5.
43745      */
43746     max_width: 2.5,
43747     
43748     /**
43749      * @cfg {integer} Draw the next point at most once per every x milliseconds. Set it to 0 to turn off throttling. Defaults to 16.
43750      */
43751     throttle: 16,
43752     
43753     /**
43754      * @cfg {integer} Add the next point only if the previous one is farther than x pixels. Defaults to 5.
43755      */
43756     min_distance: 5,
43757     
43758     /**
43759      * @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.
43760      */
43761     bg_color: 'rgba(0, 0, 0, 0)',
43762     
43763     /**
43764      * @cfg {string} Color used to draw the lines. Can be any color format accepted by context.fillStyle. Defaults to "black".
43765      */
43766     dot_color: 'black',
43767     
43768     /**
43769      * @cfg {float} Weight used to modify new velocity based on the previous velocity. Defaults to 0.7.
43770      */ 
43771     velocity_filter_weight: 0.7,
43772     
43773     /**
43774      * @cfg {function} Callback when stroke begin. 
43775      */
43776     onBegin: false,
43777     
43778     /**
43779      * @cfg {function} Callback when stroke end.
43780      */
43781     onEnd: false,
43782     
43783     getAutoCreate : function()
43784     {
43785         var cls = 'roo-signature column';
43786         
43787         if(this.cls){
43788             cls += ' ' + this.cls;
43789         }
43790         
43791         var col_sizes = [
43792             'lg',
43793             'md',
43794             'sm',
43795             'xs'
43796         ];
43797         
43798         for(var i = 0; i < col_sizes.length; i++) {
43799             if(this[col_sizes[i]]) {
43800                 cls += " col-"+col_sizes[i]+"-"+this[col_sizes[i]];
43801             }
43802         }
43803         
43804         var cfg = {
43805             tag: 'div',
43806             cls: cls,
43807             cn: [
43808                 {
43809                     tag: 'div',
43810                     cls: 'roo-signature-body',
43811                     cn: [
43812                         {
43813                             tag: 'canvas',
43814                             cls: 'roo-signature-body-canvas',
43815                             height: this.canvas_height,
43816                             width: this.canvas_width
43817                         }
43818                     ]
43819                 },
43820                 {
43821                     tag: 'input',
43822                     type: 'file',
43823                     style: 'display: none'
43824                 }
43825             ]
43826         };
43827         
43828         return cfg;
43829     },
43830     
43831     initEvents: function() 
43832     {
43833         Roo.bootstrap.BezierSignature.superclass.initEvents.call(this);
43834         
43835         var canvas = this.canvasEl();
43836         
43837         // mouse && touch event swapping...
43838         canvas.dom.style.touchAction = 'none';
43839         canvas.dom.style.msTouchAction = 'none';
43840         
43841         this.mouse_btn_down = false;
43842         canvas.on('mousedown', this._handleMouseDown, this);
43843         canvas.on('mousemove', this._handleMouseMove, this);
43844         Roo.select('html').first().on('mouseup', this._handleMouseUp, this);
43845         
43846         if (window.PointerEvent) {
43847             canvas.on('pointerdown', this._handleMouseDown, this);
43848             canvas.on('pointermove', this._handleMouseMove, this);
43849             Roo.select('html').first().on('pointerup', this._handleMouseUp, this);
43850         }
43851         
43852         if ('ontouchstart' in window) {
43853             canvas.on('touchstart', this._handleTouchStart, this);
43854             canvas.on('touchmove', this._handleTouchMove, this);
43855             canvas.on('touchend', this._handleTouchEnd, this);
43856         }
43857         
43858         Roo.EventManager.onWindowResize(this.resize, this, true);
43859         
43860         // file input event
43861         this.fileEl().on('change', this.uploadImage, this);
43862         
43863         this.clear();
43864         
43865         this.resize();
43866     },
43867     
43868     resize: function(){
43869         
43870         var canvas = this.canvasEl().dom;
43871         var ctx = this.canvasElCtx();
43872         var img_data = false;
43873         
43874         if(canvas.width > 0) {
43875             var img_data = ctx.getImageData(0, 0, canvas.width, canvas.height);
43876         }
43877         // setting canvas width will clean img data
43878         canvas.width = 0;
43879         
43880         var style = window.getComputedStyle ? 
43881             getComputedStyle(this.el.dom, null) : this.el.dom.currentStyle;
43882             
43883         var padding_left = parseInt(style.paddingLeft) || 0;
43884         var padding_right = parseInt(style.paddingRight) || 0;
43885         
43886         canvas.width = this.el.dom.clientWidth - padding_left - padding_right;
43887         
43888         if(img_data) {
43889             ctx.putImageData(img_data, 0, 0);
43890         }
43891     },
43892     
43893     _handleMouseDown: function(e)
43894     {
43895         if (e.browserEvent.which === 1) {
43896             this.mouse_btn_down = true;
43897             this.strokeBegin(e);
43898         }
43899     },
43900     
43901     _handleMouseMove: function (e)
43902     {
43903         if (this.mouse_btn_down) {
43904             this.strokeMoveUpdate(e);
43905         }
43906     },
43907     
43908     _handleMouseUp: function (e)
43909     {
43910         if (e.browserEvent.which === 1 && this.mouse_btn_down) {
43911             this.mouse_btn_down = false;
43912             this.strokeEnd(e);
43913         }
43914     },
43915     
43916     _handleTouchStart: function (e) {
43917         
43918         e.preventDefault();
43919         if (e.browserEvent.targetTouches.length === 1) {
43920             // var touch = e.browserEvent.changedTouches[0];
43921             // this.strokeBegin(touch);
43922             
43923              this.strokeBegin(e); // assume e catching the correct xy...
43924         }
43925     },
43926     
43927     _handleTouchMove: function (e) {
43928         e.preventDefault();
43929         // var touch = event.targetTouches[0];
43930         // _this._strokeMoveUpdate(touch);
43931         this.strokeMoveUpdate(e);
43932     },
43933     
43934     _handleTouchEnd: function (e) {
43935         var wasCanvasTouched = e.target === this.canvasEl().dom;
43936         if (wasCanvasTouched) {
43937             e.preventDefault();
43938             // var touch = event.changedTouches[0];
43939             // _this._strokeEnd(touch);
43940             this.strokeEnd(e);
43941         }
43942     },
43943     
43944     reset: function () {
43945         this._lastPoints = [];
43946         this._lastVelocity = 0;
43947         this._lastWidth = (this.min_width + this.max_width) / 2;
43948         this.canvasElCtx().fillStyle = this.dot_color;
43949     },
43950     
43951     strokeMoveUpdate: function(e)
43952     {
43953         this.strokeUpdate(e);
43954         
43955         if (this.throttle) {
43956             this.throttleStroke(this.strokeUpdate, this.throttle);
43957         }
43958         else {
43959             this.strokeUpdate(e);
43960         }
43961     },
43962     
43963     strokeBegin: function(e)
43964     {
43965         var newPointGroup = {
43966             color: this.dot_color,
43967             points: []
43968         };
43969         
43970         if (typeof this.onBegin === 'function') {
43971             this.onBegin(e);
43972         }
43973         
43974         this.curve_data.push(newPointGroup);
43975         this.reset();
43976         this.strokeUpdate(e);
43977     },
43978     
43979     strokeUpdate: function(e)
43980     {
43981         var rect = this.canvasEl().dom.getBoundingClientRect();
43982         var point = new this.Point(e.xy[0] - rect.left, e.xy[1] - rect.top, new Date().getTime());
43983         var lastPointGroup = this.curve_data[this.curve_data.length - 1];
43984         var lastPoints = lastPointGroup.points;
43985         var lastPoint = lastPoints.length > 0 && lastPoints[lastPoints.length - 1];
43986         var isLastPointTooClose = lastPoint
43987             ? point.distanceTo(lastPoint) <= this.min_distance
43988             : false;
43989         var color = lastPointGroup.color;
43990         if (!lastPoint || !(lastPoint && isLastPointTooClose)) {
43991             var curve = this.addPoint(point);
43992             if (!lastPoint) {
43993                 this.drawDot({color: color, point: point});
43994             }
43995             else if (curve) {
43996                 this.drawCurve({color: color, curve: curve});
43997             }
43998             lastPoints.push({
43999                 time: point.time,
44000                 x: point.x,
44001                 y: point.y
44002             });
44003         }
44004     },
44005     
44006     strokeEnd: function(e)
44007     {
44008         this.strokeUpdate(e);
44009         if (typeof this.onEnd === 'function') {
44010             this.onEnd(e);
44011         }
44012     },
44013     
44014     addPoint:  function (point) {
44015         var _lastPoints = this._lastPoints;
44016         _lastPoints.push(point);
44017         if (_lastPoints.length > 2) {
44018             if (_lastPoints.length === 3) {
44019                 _lastPoints.unshift(_lastPoints[0]);
44020             }
44021             var widths = this.calculateCurveWidths(_lastPoints[1], _lastPoints[2]);
44022             var curve = this.Bezier.fromPoints(_lastPoints, widths, this);
44023             _lastPoints.shift();
44024             return curve;
44025         }
44026         return null;
44027     },
44028     
44029     calculateCurveWidths: function (startPoint, endPoint) {
44030         var velocity = this.velocity_filter_weight * endPoint.velocityFrom(startPoint) +
44031             (1 - this.velocity_filter_weight) * this._lastVelocity;
44032
44033         var newWidth = Math.max(this.max_width / (velocity + 1), this.min_width);
44034         var widths = {
44035             end: newWidth,
44036             start: this._lastWidth
44037         };
44038         
44039         this._lastVelocity = velocity;
44040         this._lastWidth = newWidth;
44041         return widths;
44042     },
44043     
44044     drawDot: function (_a) {
44045         var color = _a.color, point = _a.point;
44046         var ctx = this.canvasElCtx();
44047         var width = typeof this.dot_size === 'function' ? this.dot_size() : this.dot_size;
44048         ctx.beginPath();
44049         this.drawCurveSegment(point.x, point.y, width);
44050         ctx.closePath();
44051         ctx.fillStyle = color;
44052         ctx.fill();
44053     },
44054     
44055     drawCurve: function (_a) {
44056         var color = _a.color, curve = _a.curve;
44057         var ctx = this.canvasElCtx();
44058         var widthDelta = curve.endWidth - curve.startWidth;
44059         var drawSteps = Math.floor(curve.length()) * 2;
44060         ctx.beginPath();
44061         ctx.fillStyle = color;
44062         for (var i = 0; i < drawSteps; i += 1) {
44063         var t = i / drawSteps;
44064         var tt = t * t;
44065         var ttt = tt * t;
44066         var u = 1 - t;
44067         var uu = u * u;
44068         var uuu = uu * u;
44069         var x = uuu * curve.startPoint.x;
44070         x += 3 * uu * t * curve.control1.x;
44071         x += 3 * u * tt * curve.control2.x;
44072         x += ttt * curve.endPoint.x;
44073         var y = uuu * curve.startPoint.y;
44074         y += 3 * uu * t * curve.control1.y;
44075         y += 3 * u * tt * curve.control2.y;
44076         y += ttt * curve.endPoint.y;
44077         var width = curve.startWidth + ttt * widthDelta;
44078         this.drawCurveSegment(x, y, width);
44079         }
44080         ctx.closePath();
44081         ctx.fill();
44082     },
44083     
44084     drawCurveSegment: function (x, y, width) {
44085         var ctx = this.canvasElCtx();
44086         ctx.moveTo(x, y);
44087         ctx.arc(x, y, width, 0, 2 * Math.PI, false);
44088         this.is_empty = false;
44089     },
44090     
44091     clear: function()
44092     {
44093         var ctx = this.canvasElCtx();
44094         var canvas = this.canvasEl().dom;
44095         ctx.fillStyle = this.bg_color;
44096         ctx.clearRect(0, 0, canvas.width, canvas.height);
44097         ctx.fillRect(0, 0, canvas.width, canvas.height);
44098         this.curve_data = [];
44099         this.reset();
44100         this.is_empty = true;
44101     },
44102     
44103     fileEl: function()
44104     {
44105         return  this.el.select('input',true).first();
44106     },
44107     
44108     canvasEl: function()
44109     {
44110         return this.el.select('canvas',true).first();
44111     },
44112     
44113     canvasElCtx: function()
44114     {
44115         return this.el.select('canvas',true).first().dom.getContext('2d');
44116     },
44117     
44118     getImage: function(type)
44119     {
44120         if(this.is_empty) {
44121             return false;
44122         }
44123         
44124         // encryption ?
44125         return this.canvasEl().dom.toDataURL('image/'+type, 1);
44126     },
44127     
44128     drawFromImage: function(img_src)
44129     {
44130         var img = new Image();
44131         
44132         img.onload = function(){
44133             this.canvasElCtx().drawImage(img, 0, 0);
44134         }.bind(this);
44135         
44136         img.src = img_src;
44137         
44138         this.is_empty = false;
44139     },
44140     
44141     selectImage: function()
44142     {
44143         this.fileEl().dom.click();
44144     },
44145     
44146     uploadImage: function(e)
44147     {
44148         var reader = new FileReader();
44149         
44150         reader.onload = function(e){
44151             var img = new Image();
44152             img.onload = function(){
44153                 this.reset();
44154                 this.canvasElCtx().drawImage(img, 0, 0);
44155             }.bind(this);
44156             img.src = e.target.result;
44157         }.bind(this);
44158         
44159         reader.readAsDataURL(e.target.files[0]);
44160     },
44161     
44162     // Bezier Point Constructor
44163     Point: (function () {
44164         function Point(x, y, time) {
44165             this.x = x;
44166             this.y = y;
44167             this.time = time || Date.now();
44168         }
44169         Point.prototype.distanceTo = function (start) {
44170             return Math.sqrt(Math.pow(this.x - start.x, 2) + Math.pow(this.y - start.y, 2));
44171         };
44172         Point.prototype.equals = function (other) {
44173             return this.x === other.x && this.y === other.y && this.time === other.time;
44174         };
44175         Point.prototype.velocityFrom = function (start) {
44176             return this.time !== start.time
44177             ? this.distanceTo(start) / (this.time - start.time)
44178             : 0;
44179         };
44180         return Point;
44181     }()),
44182     
44183     
44184     // Bezier Constructor
44185     Bezier: (function () {
44186         function Bezier(startPoint, control2, control1, endPoint, startWidth, endWidth) {
44187             this.startPoint = startPoint;
44188             this.control2 = control2;
44189             this.control1 = control1;
44190             this.endPoint = endPoint;
44191             this.startWidth = startWidth;
44192             this.endWidth = endWidth;
44193         }
44194         Bezier.fromPoints = function (points, widths, scope) {
44195             var c2 = this.calculateControlPoints(points[0], points[1], points[2], scope).c2;
44196             var c3 = this.calculateControlPoints(points[1], points[2], points[3], scope).c1;
44197             return new Bezier(points[1], c2, c3, points[2], widths.start, widths.end);
44198         };
44199         Bezier.calculateControlPoints = function (s1, s2, s3, scope) {
44200             var dx1 = s1.x - s2.x;
44201             var dy1 = s1.y - s2.y;
44202             var dx2 = s2.x - s3.x;
44203             var dy2 = s2.y - s3.y;
44204             var m1 = { x: (s1.x + s2.x) / 2.0, y: (s1.y + s2.y) / 2.0 };
44205             var m2 = { x: (s2.x + s3.x) / 2.0, y: (s2.y + s3.y) / 2.0 };
44206             var l1 = Math.sqrt(dx1 * dx1 + dy1 * dy1);
44207             var l2 = Math.sqrt(dx2 * dx2 + dy2 * dy2);
44208             var dxm = m1.x - m2.x;
44209             var dym = m1.y - m2.y;
44210             var k = l2 / (l1 + l2);
44211             var cm = { x: m2.x + dxm * k, y: m2.y + dym * k };
44212             var tx = s2.x - cm.x;
44213             var ty = s2.y - cm.y;
44214             return {
44215                 c1: new scope.Point(m1.x + tx, m1.y + ty),
44216                 c2: new scope.Point(m2.x + tx, m2.y + ty)
44217             };
44218         };
44219         Bezier.prototype.length = function () {
44220             var steps = 10;
44221             var length = 0;
44222             var px;
44223             var py;
44224             for (var i = 0; i <= steps; i += 1) {
44225                 var t = i / steps;
44226                 var cx = this.point(t, this.startPoint.x, this.control1.x, this.control2.x, this.endPoint.x);
44227                 var cy = this.point(t, this.startPoint.y, this.control1.y, this.control2.y, this.endPoint.y);
44228                 if (i > 0) {
44229                     var xdiff = cx - px;
44230                     var ydiff = cy - py;
44231                     length += Math.sqrt(xdiff * xdiff + ydiff * ydiff);
44232                 }
44233                 px = cx;
44234                 py = cy;
44235             }
44236             return length;
44237         };
44238         Bezier.prototype.point = function (t, start, c1, c2, end) {
44239             return (start * (1.0 - t) * (1.0 - t) * (1.0 - t))
44240             + (3.0 * c1 * (1.0 - t) * (1.0 - t) * t)
44241             + (3.0 * c2 * (1.0 - t) * t * t)
44242             + (end * t * t * t);
44243         };
44244         return Bezier;
44245     }()),
44246     
44247     throttleStroke: function(fn, wait) {
44248       if (wait === void 0) { wait = 250; }
44249       var previous = 0;
44250       var timeout = null;
44251       var result;
44252       var storedContext;
44253       var storedArgs;
44254       var later = function () {
44255           previous = Date.now();
44256           timeout = null;
44257           result = fn.apply(storedContext, storedArgs);
44258           if (!timeout) {
44259               storedContext = null;
44260               storedArgs = [];
44261           }
44262       };
44263       return function wrapper() {
44264           var args = [];
44265           for (var _i = 0; _i < arguments.length; _i++) {
44266               args[_i] = arguments[_i];
44267           }
44268           var now = Date.now();
44269           var remaining = wait - (now - previous);
44270           storedContext = this;
44271           storedArgs = args;
44272           if (remaining <= 0 || remaining > wait) {
44273               if (timeout) {
44274                   clearTimeout(timeout);
44275                   timeout = null;
44276               }
44277               previous = now;
44278               result = fn.apply(storedContext, storedArgs);
44279               if (!timeout) {
44280                   storedContext = null;
44281                   storedArgs = [];
44282               }
44283           }
44284           else if (!timeout) {
44285               timeout = window.setTimeout(later, remaining);
44286           }
44287           return result;
44288       };
44289   }
44290   
44291 });
44292
44293  
44294
44295